Installing Ruby on Rails on Ubuntu and Windows XP at the same time under Parallels
Installing Rails on Ubuntu and Windows XP at the same time under Parallels

Now that I have my Ubuntu Linux and Windows XP virtual machines running in Parallels (see Street cred? No: Geek cred (Part 1 of 2)), it's time to set them up to run Ruby on Rails. Why? You know... because.

Again, I'm not going to go into excruciating detail, but instead reference the sites that helped me with this and the problems I faced. Luckily there were only a few.

Windows XP + Ruby on Rails == Easy

Here I pretty much followed the instructions on the main Ruby on Rails site. As much as the Rails community loooooooooves their Macs, getting Rails up and running on Windows is much, much easier and faster than any other OS. No ports, no compiling, no apt-get install voodoo. It's just double-click and next-next-next-next-next...

  • download and install using the ruby windows .exe installer. Make sure you select "enable gems" when asked!
  • download and install mysql
  • gem install rails -y
  • gem install mysql -y
  • gem install mongrel_cluster -y

Ubuntu Linux + Rails = Funkadillio

The guide to installing Ruby on Rails on Ubuntu Dapper Drake at Urban Puddle was a very good guide, but I still had troubles. The issues had the same theme: I needed development/compilation tools, which I had not installed as part of the Ubuntu install process. Here are the other things I needed to do:

Ruby Dev Library

I got the following error when trying to gem install mysql -y and gem install mongrel_cluster -y:

extconf.rb:1:in 'require': no such file to load - - mkmf (LoadError)

Solution: install ruby1.8-dev:

sudo apt-get install ruby1.8-dev

make And gcc

The mysql and mongrel gems also complained about make and gcc not being found:

make
sh: make: command not found

and

make: gcc: Command not found

If you get this, install them both:

sudo apt-get install make
sudo apt-get install gcc

Mysql Client Dev Library

I ended up installing this, too, but I'm not sure if it actually fixed anything.

sudo apt-get install libmysqlclient12-dev

That's it so far. I'm planning on setting up the Ubuntu image as a Capistrano deploy target and treating it as a test server.

OS X running Windows XP and Ubuntu Linux virtual machines in ParallelsI'm finally getting around to playing with Parallels Desktop for Mac, specifically the free release candidates for the next version. My geek-meter is rising with every moment. Here are some of the cool things and problems I found along the way. I'm not going to detail a complete install guide, but I will reference the ones I used and give credit where credit is due.

Windows XP on Parallels

The Parallels Release Candidate includes this nice feature: if you already have a Windows installation on your Mac from using Boot Camp, Parallels will let you run it in a virtual machine. This gives you the best of both worlds: a true hardware-accelerated Windows install for games (why else?) that you can also fire up in Parallels when you want to see if your CSS works in IE6 how badly your CSS is broken in IE6.

Problems with Windows XP on Parallels

Parallels wouldn't start my Boot Camp image immediately, instead giving me this error:

Parallels Desktop cannot find necessary drivers to configure your Boot Camp partition. Please refer to Help> Parallels Desktop Help>Using Boot Camp Windows XP Installation for troubleshooting*

... and so I did. Help> Parallels Desktop Help>Using Boot Camp Windows XP Installation suggested the following:

  1. Boot into Windows XP via Boot Camp
  2. Insert Microsoft Windows XP installation disc which was used for this Windows XP installation.
  3. Locate the folder i368 on the disc and open it. Copy the following files: driver.cab and any of spN.cab files (sp1.cab or sp2.cab, etc depending on service package installed) to the folder: C:\WIndows\Driver Cache\i386.*

... and so I did. The virtual machine worked fine the next time I ran it, no problems.

Ubuntu Linux on Parallels

Nothing very exciting here. When creating the virtual machine, I created a "custom" installation of "Linux/Other Linux kernel 2.6"; this worked fine (mostly... keep reading.) Download yourself an ISO disk image from Ubuntu's download site or bittorrent one. I'm using Dapper Drake (6.06 with Long Term Support). When prompted to "Insert Linux Kernel 2.6 installation CD," choose "More Options" and specify the location of your downloaded ISO.

Problems with Ubuntu Linux on Parallels

I had to install it twice for some reason, which means that I had to delete the first virtual machine I crated and start from scratch. There was some kind of fatal error that I didn't pay attention to at the time, and thus I cannot tell you what it was; now unhelpful of me.

Next up: setting up the virtual machines for Ruby on Rails development.

*Text is most likely © Parallels. Hopefully they will be nice and let me help people help themselves and also sell more of their product.

Check Out the Pivotal Labs Blog

February 6th, 2007

... because it's where I work. We're still working on content.

http://blog.pivotalsf.com

We Have Been Macssimilated

January 31st, 2007

And another one joins the Borg -- I bought a MacBook Pro. Though using a Mac is rather slow for me at this point since my keyboard navigation is hard-wired into my nerves system, I see to many advantages to avoid it any longer. Speed, the fact that it's a hard-core Unix system, stability, cool-kid factor... the list goes on and on. But this was the real ringer: a coworker of mine is working at a client with several hard-core sysadmin hackers, and guess what they have switch to? No, not a Dell or ThinkPad with some crazy, hand-compiled version of Linux, but Macs.

On a non-techie note, buying this thing was a wonderful experience. I have been watching the Apple Refurbished Store for the latest Core 2 Duo MacBook Pros and they started appearing a few weeks ago. Same warentee, so why not save a few bucks? Anyway, I dropped the hammer on one yesterday morning, it shipped that same afternoon, and was delivered the next day, all for free shipping. Good service!

Now I have to install Ruby, Ruby on Rails, MySQL... oh boy.

We were looking for the latest Rails Release Candidate to freeze into a aproject and ended up in Tags dir (http://dev.rubyonrails.org/browser/tags), and found this:

  • rel_1-2-0 -- 5983 -- 9 hours --david: Fix nodoc breaking adapters (closes #7161)
  • rel_1-2-1 -- 5990 -- 3 hours -- david: Tagged Rails 1.2.1

So, it's hi-1.2-bye-1.2-hi-1.2.1?

NOTE: You might have been redirected here. Don't worry! I've moved my active blog to 40. (with egg)

UPDATE: fixed my bad code...

Here follows my summary of "Why there is no Smalltalk-like [or Java-like] IDE for Ruby," compiled from the over 100 responses the threads on comp.lang.ruby and comp.lang.smalltalk,and the email thread on the Ruby on Rails mailing list . I will do my best not to bodge everything up. Interestingly, many comments on the newsgroups and most of the mailing list revolved around the IDEs are Good vs IDEs are Evil debate, which I might summarize at a later time.

I'll do my best with this! Be gentle.

Code Completion, Refactoring, etc.

It seems that the difficulty of Ruby code completion, refactoring,and pretty much every issue can be summed up as this: Ruby has no abuild-in concept of a live runtime "image" at development time, nostatic typing and method argument/return typing that clearly defines objects and methods, and no compiled artifacts that can be mined for data.

Smalltalk vs. Java

In a very, very small nutshell:

Smalltalk, like Ruby, is loosely-typed and (some say) compiled at runtime, but "runtime" is always happening. Smalltalk is alwayslive, always in a particular state at development time; the concept of"runtime" rather melts away, as there is nothing to really contrastagainst it. The state of the entire Smalltalk environment is saved in an ever-evolving "image" that can be mined for ample data to perform refactorings, code completion, and such.

Java, unlike Ruby, is a compiled, statically typed language, and these two features give an IDE ample data about Java objects at development time.

Challenge #1: Language differences between Ruby and Smalltalk

Ruby and Smalltalk are different beasts, even though they share many language constructs and philosophies, most notably loose typing, blocks, and the ability to "reopen" classes and instances at runtime to add methods, etc. But Smalltalk knows about it's development environment (link):

The Smalltalk language 'knows about' its development environment at quite a deep level. The fact that Ruby does not have any built-in knowledge of an environment makes it relatively hard work to make the language work 'seamlessly' in an IDE - I speak as someone who is trying to do just that :)

In other words, Smalltalk is tightly coupled to it's development environment. Smalltalk IDEs keep track of the state of things by keeping a constantly-evolving "image" of the code, which contains an enormously amount of metadata about the state of classes, methods, etc. For example, take the following example Ruby code, which changes a class named Shoe if a condition is met:

class Shoe
...
end

class ShoeTweaker
   def tweak_shoe
      if (self.i_feel_like_it)
         class << Shoe
            def laces #reopen class Shoe and change it!
               ...
            end
         end
      end
   end
end

If this code were saved in a Smalltalk-like IDE, this possible manipulation of the Shoe class would be stored in an "image", and the IDE (and the Smalltalk runtime environment itself) would forevermore know about this possible change to Shoe. Obviously, Ruby has no such facility built into the language itself. But, as pointed out in in the newsgroup thread, there is nothing preventing an IDE maker from providing this functionality (link):

... [In Smalltalk] methods and classes are /always/ added at runtime -- just as in Ruby.

The difference is that Smalltalk doesn't initialize itself by reading and executing a sequence of class- and method-creation expressions. The definitions already existed when your restarted your image. If you define new classes or methods, /then/ these are compiled at runtime and added to the image, so that if you save the image, |they will be saved too.

It makes Smalltalk /look/ as if it's like (say) C++ -- with a list of classes and methods which "just exist" and you browse over them in the IDE.... It would certainly be possible to create something similar for Ruby, but first it would have to have some way of preserving the state of the /entire/ computation between runs.

Hmm... an image-saving IDE would get around Ruby's interpreted nature, a very common theme pointing to why it's so hard to create a good IDE for Ruby. In Ruby, there are no compiled files or bytecodes to mine for metadata about Classes. The entire object tree is recreated as the script is executed. That said, the ruby Interactive Ruby Shell (irb) gets around this somehow, and if the irb's state could be saved, or always running, or if the editor in an IDE was the irb... This might be a great opportunity to implement a Smalltalk-like image/model paradigm for a Ruby IDE, but not make the same mistake choice :) that Smalltalk made to tightly couple the development image to the language and runtime itself. This might even be better (link):

... there are positive points about decoupling the programming from the environment. ... it is quite possible to introduce behavior that may have seemed neat this time last week but does not seem anything like as good now...

Challenge #2: Language differences between Ruby and Java

This one seems pretty easy to understand:

  • Ruby is loosely-typed, while Java is statically-typed
  • Ruby is interpreted at runtime while Java is compiled

As for the compiled-nature issue, reference the previously written stuff about Smalltalk images for info about how Smalltalk got around this. But, there are some interesting details about how Java IDEs have an advantage (link):

... In Java the IDEs use the front end of a compiler to build an abstract model of your program. The refactorings are actually applied to the model in a provably correct way, and then the changes are reflected back in the code. Having things like method generators, method_missing and dynamic structs makes this impossible to do without being inside a running program that can be inspected, which just won't work for a ruby IDE unless you turn it into a live memory type environment.

Java's statically-typed nature makes the world a relatively easy place to live in for IDEs: A class has X methods, implements Y interfaces, and inherits from Z classes. Method signatures are typed as well: in Java, lookUpNames(List names) takes an object of type List, while Ruby's look_up_names(names) takes... who knows? As long as that object don't blow up if messages are sent to it later, then we're happy. This makes code completion a little hard. Nobody had an easy answer for this (or even addressed it) but I'm sure there are some brute-force ways of implementing this, such as "guessing" Classes, or having the developer pick the Class once and remember it, or simply remembering a linked-list of method calls ('person' was once followed by 'name' and also by 'address', so those will be in the list of possible method calls...)

I'm sure I'm leaving stuff out, and if there are glairing mistakes I'll update this later. Hopefully those who were confused (like me) about why Ruby IDE development is so hard will find this useful.

1 Comments (from old blog):

At 10/29/2006 9:57 PM, Chad Woolley said…

There was a thread I saw recently where someone proposed that the unit tests could be leveraged to help exercise code in the IDE, and help determine the dynamic runtime state of the program. This is interesting, but even with 100% code coverage, you still couldn't ensure that every logic path through your program is covered (Unlike EMMA for java, which does block-level coverage metrics, rcov still only does line-level, and even that imperfectly).

Also, it seems like it would help a lot if IDEs were smart enough to find the areas where dynamic code might cause problems with refactoring (instance_eval, method_missing, etc), and either attempt to automatically handle them, or at least point them out to you and let you handle them.

-- Chad

Tramendus response to my posts to the newsgroups and mailing lists! As of this writing:

I'll be combiling a list of interesting items soon, but overall themes include:

  • Smalltalk "knows" about it's development environment, helping it overcome a lot of issues that Ruby has.
  • Java is combiled + statically typed, helping it overcome issues that Ruby has.
  • Smalltalk and Java have funded projects and are supported by the business community.
  • That said, most of the issues can be overcome in time.
  • Update: people are passionate about their favorite text editors.
  • Update: the "we don't need IDEs" vs. "IDEs are almost essential" debate rages on.

I finally did it: I posted my "Smalltalkers and Rubyists unite for IDE support!" post to comp.lang.ruby, comp.lang.smalltalk, ruby-talk@ruby-lang.org, and rails@lists.rubyonrails.org. We'll see if I get trounced by the communities.

Hi all --

I shout my question to the entire Ruby + Smalltalk community: Smalltalk has had amazing IDEs for decades, why not Ruby? Smalltalkers, Ruby needs your help!

I'm hoping to start a centralized discussion about this topic, since my searches have only turned up scattered comments.

Ruby should have IDE support approaching Smalltalk's based on the following gross generalization: Ruby and Smalltalk are pretty much the same. Yes, I know there are many differences, and not trying to provoke a Ruby vs. Smalltalk cage-match, but based on language features and constructs, they are very similar.

What is holding Ruby back? How has Smalltalk overcome the issues? What can Ruby tool builders (such as the RadRails folks and, hopefully, me) learn from the Smalltalk IDE builders? Reasons I've heard for Ruby's lack of tool support include:

  • Ruby is not a compiled language
  • Ruby does not execute in a VM or run-time
  • Ruby is a loosely-typed language and has blocks, etc.
  • Nobody really cares enough about a Ruby IDE tomake one
  • vi is all you need!

Regarding the compiled language and VM arguments: what about Ruby's irb? Regarding loose typing, blocks, etc: Smalltalk has these! I don't pretend to understand all of the issues, but I want to learn. Unless there is something I simply don't "get", it seems that the Ruby community does not care or see the benefit of real tool support, which leads me to believe that (again) the Smalltalk community is not very interested in Ruby.

I've only been working with Ruby for 8 months after 7 years of Java, but I almost feel like a Smalltalker by association, having worked with, and for, Old Dudes Who Know Smalltalk (yes, I said it) my entire career. I've stepped back into the stone age regarding IDE support after using VisualAge for Java, Eclipse, and InilliJ IDEA. No refactoring, no fast debugger support, not even code-completion/suggestion. To the current tools, Ruby is text to colorize.

Smalltalkers, you've cracked this nut years ago, help us understand how to do it again in Ruby!

-- Joe

http://www.josephmoore.net/

Pop quiz: your browsing some ruby code (which you often do in your spare time) and see the following:

    party_like[:its] = "1999"
    puts party_like[:its]

What will be put to the console when these lines of ruby execute? You might be saying that since party_like seems to be some kind of Hash or a Hash-like-thingie, you should get...

    #=> 1999

At least 99% of the time you'd be right, unless the code is actually the cookies method in a Rails Controller:

    cookies[:its] = "1999"
    puts cookies[:its]

    #=> nil

BUWAHHHHHH? Cookies isn't a Hash, although the cookies method does return a CookieJar object, which extends Hash. CookieJar mangles the expected Hash methods of [] and []= with these gotchas:

  • The CookieJar object does not represent one set of cookies, it represents 2 sets: the incoming cookies from the browser, and the outgoing cookies that will be sent back to the client
  • cookies[:key]gets you the incoming cookies from the client
  • cookies[:key]= value sets outgoing cookies that will be sent back to the client
  • ... all of which mean that cookies[:its] = "1999" will not allow you to retrieve that value with cookies[:its].

But wait, it gets more confusing. In a test you do the following:

    def test_cookies
      @request.cookies[:its] = "1999"
       get :index
    end

And in your controller:

    class CookiesController < ApplicationController
      def index
        puts cookies[:its]           #=> nil
        puts cookies.inspect    #=> {:its=>"1999"}
        render :text => ""
      end
    end

The cookies.inspect confusingly returns {:its=>"1999"}, which seems impossible: I put the value in and get nil when I ask for it, but if I inspect the object to see what's up, it's there! Right? Not, not really. The incoming cookies are displayed, not the outgoing. In a typical test-fail-debug scenario, this is a head-scratcher.

For the love of SANITY: do not override methods on core objects to do completely unexpected stuff, and especially don't mangle the expected functionality of operators! I realize that with languages like ruby you can play God, overriding and changing the implementation of anything you please, but some things are sacred: +,-, [], []=, etc. + should add stuff. - should remove stuff. And objects that have [] and []= should let you get stuff with [key] and set them with [key]=value.


1 Comments (from old blog):

At 10/13/2006 5:33 PM, Joshua Jarman said…

I have a slightly different take on the cookies object. It is a hash, with a wormhole inside connecting it to the last and next browser requests. Essentially it doesn't exist in the now. ;-)

Cookies are decoupled. When you set the value it starts a chain of events. The server needs to send the cookie to the browser and the browser has to return the cookie. Then you can read the value.

    cookies[:its] = "1999"

    *server response
    *browser request

      p cookies[:its]
      #=> "1999"

You can use "app" in tests (or the console) to simulate user sessions and to send and receive cookies, which will allow you to get your values back out and check them.

Example:
http://clarkware.com/cgi/blosxom/2006/04/04

So maybe more twisted and misunderstood then broken.

Cheers,
Josh

I went to the SF Ruby Meetup for the first time on June 12; great turnout by all accounts, about 80 people or so. The first presenter, Derek Haynes (Highgroove Studios) talked about building the perfect Rails team, and we've done so at my company if you follow his formula. But more interesting to me is this trend, which other might have known for a long time: design-first development.

Here's what I've gleaned from the few talks I've heard about this: in design-first development, server side modeling is almost completely ignored in favor of really nailing the UI design, flow, features, and user experience. Developers/Designers (devigners?) go strait for the HTML, CSS, and JavaScript, mocking out any AJAX calls to make the design seem like it's really talking to the server. This is iterative, but not in the sense that the entire end-to-end system is designed and refactored along the way. Only after the design is whiz-bang'ed and solid do the developers bolt on (my words) a model layer. I've heard this pushed the Haynes talk, and also by web design guru Dan Cederholm (Bullet Proof Web Design and SimpleBits), and by some members of my own development team.

This is completely foreign to me. I'm model guy, an end-to-end guy. I work from the domain model layer out towards the UI, and get a very simple thing working first, then refactor towards the ultimate design. And even then my UI is going too look like cat puke until I feel like I can devote the time to really getting it right, but usually after the model is solid. So my first reaction it to recoiled from design-first development and start saying works like "scaling!" and "domain-driven development!" and "do the simplest thing possible!" and "YAGNI!" and "models should be understood by our customers!". But is this justified? On most of the Rails projects we're developing for our clients, we knock out the server side stuff, including DB, models, and controllers very quickly (and yes, test the hell out of them), then spend the majority of time in Jails (JavaScript for Rails). And when we have had nice mockups from clients we have stuck to them, for the most part. I'll have to talk to some DFD'ers and see how the bolted-on models hold up.


1 Comments (from old blog:

At 6/16/2006 9:55 AM, DrydenMaker said…

I tend to agree with your thoughts. The difference that is evolving is that the UI is putting on weight and there is more programming going on there that makes the server side easier. We are moving toward the 'programmable web'.

DrydenMaker http://webdevsnob.blogspot.com/

NOTE: You might have been redirected here. Don't worry! I've moved my active blog to 40. (with egg)

Normally I don't spam/link post, but this is too important not to shout out to the world: Ruby development, with debugging support, and soon with Rails support, in... wait for it... wait for it...Visual Studio 2005! Check out Steel here:

If you talk to any of my coworkers, they will tell you that I am an Eclipse zealot. I love Eclipse. I love and understand it's way of thinking, and I am the most productive with this IDE. Well, at least I used to be when I was doing full time Java development with Eclipse's Java IDE, which is actually a massive Eclipse plugin. Now I'm a full time Ruby on Rails developer using the painfully poor Ruby/Rails plugins for Eclipse: RDT and RadRails. RDT and RadRails are developed by Some Dudes who are busy either working at real jobs that don't include developing free IDEs for me, or studying for finals and being excited that they are old enough to drink beer. Point being that the IDE support for Ruby and/or Rails is stone age at this point, and 99% of the features I use for Ruby development are not RDT/RadRails features at all, but default Eclipse feature or features from other plugins, such as the HTML, JavaScript, and CSS support from the excellent J2EE Standard Tools project.

If Steel can bring to Ruby/Ruby on Rails development even a small portion of productivity gaining features that I sorely miss from Eclipse's Java IDE, then I will burn my Eclipse Fan Club membership card and send the ashes to Erich Gamma, then buy Visual Studio for my workplace with my own money.

Can you tell I'm excited about this? I'm switching back and forth between writing this post and reading the Sapphire In Steel home page. I've just read the About Us page and I'm getting more insight into why these guys "get it". Here are some snippits:

has spent the last twenty years...

has programmed in languages ranging from Delphi and Java to C# and Smalltalk...

since 1988...

Ah, there it is -- These are Old Dudes! I love Old Dudes! And I really love Old Dudes Who Know Smalltalk! I was nurtured, sculpted, and brainwashed by Old Dudes Who Know Smalltalk from my very first day as a professional programmer, and they universally "get it". Young whipper-snappers out there, take note: if you ever here some Old Dude say the words "in Smalltalk you could blah blah blah" or "In VisualWorks you could yada yada", spend as much time with this person as possible. You will learn more from them about software development than the Young Dude who only wears black and thinks that the bash shell is "too bloated".

And what does "get it" mean? Maybe I'll get into that some other time (it will be ugly, as this is one are where I am very opinionated), but the important thing is this: these guys don't come from the school of web scripting hackery in vi, they come from the land of building real enterprise applications, where real tool support is appreciated. And at this point in the Ruby IDE game, I'd place my bets on them to produce the first a truly usefully development tool.


1 Comments (from old blog):

At 9/30/2006 8:18 AM, Torv said…

Well, finally you get it ;)

www.squeak.org

NOTE: You might have been redirected here. Don't worry! I've moved my active blog to 40. (with egg)

We faced and common problem on our project recently: we wanted to deploy two public-facing versions of our Ruby on Rails application at the same time -- demo version and a production version. There was a catch, of course: the demo and production deployments needed to expose different functionality to users. We used routes.rb to solve this, but found unit testing the route changes difficult until we discovered the with_routing method in the Rails test framework.

The Details:

Let's say you have a whole lot of functionality that is not ready for prime-time, but you still want to expose a password-protected (demo) version of this application to our client. But, since you paid so much for that domain name, you’d love it if people could go to the production site and see the “Coming Soon!”, “About Us” and “Info” sections. As developers, we certainly don’t want to create two versions of the application, or sprinkle environment/configuration checking code all over the app in an effort to keep certain sections locked-down in “production” mode, while open in “demo” mode.

One solution in Ruby on Rails is to add mappings to routes.rb that check the ENV['RAILS_ENV'], then route traffic appropriately depending on the currently running RAILS_ENV. As good test driven developers, we’ll test-drive these changes.

Oh, and before you ding me for the DRY principle, hold your horses… we’ll address that towards the end.

Demo – Everything is exposed (but password-protected): Production: Only “Coming Soon!”, “About Us” and “Info” is allowed:

require File.dirname(__FILE__) + '/../test_helper'

class RoutesTest
  def test_demo_routes
    opts ="{:controller"> "movie", :action => "show", :id => "1"}
    assert_routing "movie/show/1", opts, "should have normal routing"
  end

  def test_production_routes
    ENV['RAILS_ENV'] = 'production'
    contact_route = {:controller => "about_us", :action => "contact"}
    assert_routing "about_us/contact", contact_route, "contact is allowed"

    sign_up_route = {:controller => "info", :action => "sign_up"}
    assert_routing "info/sign_up", sign_up_route, "sign up is allowed"

    path = "movie/show/1"
    coming_soon_route = {:anything => path.split("/"), :controller => 'temp', :action => 'coming_soon'}
    assert_routing(path, coming_soon_route, "everything else goes to coming_soon")
  end
end

test_demo_routes passes since it uses the default generated routes, but test_production_routes fails as expected: we haven’t written the code that treats production differently yet. Let’s edit routes.rb next.

routes.rb (just the goodies, leaving out the comments and block)

map.connect 'info/:action', :controller => 'info'
map.connect 'about_us/:action', :controller => 'about_us'

map.connect ':controller/:action/:id' unless ENV['RAILS_ENV'] == 'production'

map.connect '*anything', :controller => 'temp', :action => "coming_soon"

Now we run our tests again and guess what? test_production_routes still fails. Why? Because routes.rb is loaded once at system startup and is not reevaluated; in other words, the routes are built once and only once, based on the RAILS_ENV at startup time. And since our RAILS_ENV was ‘test’ when we launched our tests, ENV['RAILS_ENV'] == 'production' evaluated to false, and thus the default, wide-open route is in effect for test_production_routes. So, how do we test this stuff?

with_routing, and no, that’s not a typo: with_routing is a method in the Rails testing framework that allows you to override the routes build at system startup. At the time of this writing, the only mention of with_routing in the entire Googleverse is its own sparse documentation, so I’m not sure how many people use it. Here’s how it works:

  with_routing do |map|
    map.draw do 
      #add code here that would
   #normally go in routes.rb
      map.connect 'some_controller/:action/:id'
    end
    #assert some stuff while still in the with_routing block
    assert_routing(x,y, “this applies while in with_routing”)
  end

Let’s edit test_production_routes:

    def test_production_routes1
      ENV['RAILS_ENV'] = 'production'
      with_routing do |map|
          map.draw do 
            map.connect 'info/:action', :controller => 'info'
        map.connect 'about_us/:action', :controller => 'about_us'

        map.connect ':controller/:action/:id' unless ENV['RAILS_ENV'] == 'production'

        map.connect '*anything', :controller => 'temp', :action => "coming_soon"
      end

    contact_route = {:controller => "about_us", :action => "contact"}

    assert_routing "about_us/contact", contact_route, "contact is allowed"

    sign_up_route = {:controller => "info", :action => "sign_up"}
    assert_routing "info/sign_up", sign_up_route, "sign up is allowed"

    path = "movie/show/1" coming_soon_route = {:anything => path.split("/"), :controller => 'temp', :action => 'coming_soon'}
    assert_routing(path, coming_soon_route, "everything else goes to coming_soon")
  end 
end

It’s kind of ugly, but I wanted to add enough detail to show how it works. Wrapping all of the routing and asserts in with_routing allows us to change the RAILS_ENV within the test, and the routes will obey the change.

Now just copy and paste those routes within with_routing into routes.rb… and keep them both in synch every time you change them… bleh, I don’t think so. That violates DRY (Don’t Repeat Yourself) for no good reason. What we really need to do is be able to reference the routes inside routes.rb whenever we want to. Let’s wrap them in a Class and method for easy referencing.

(Note: I’m not the best ruby programmer, so there might be a way of doing this that is more ruby-friendly, but since I’m coming into this after years of Java my first instinct is to wrap stuff in methods.)

routes.rb:

class RoutesLoader
def self.build_routes(map)
  map.connect 'info/:action'
  map.connect 'about_us/:action'

  map.connect ':controller/:action/:id'
  unless ENV['RAILS_ENV'] == 'production'

  map.connect '*anything', :controller => 'temp', :action => "coming_soon"
end
end

ActionController::Routing::Routes.draw do |map|
    RoutesLoader.build_routes(map)
end

This allows us to call RoutesLoader.build_routes(map) inside test_production_routes:

def test_production_routes
  ENV['RAILS_ENV'] = 'production'
  with_routing do |map|
      map.draw do 
        RoutesLoader.build_routes(map)
      end
    contact_route = {:controller => "about_us", :action => "contact"}
    …..

I hope this at least gives a bit more documentation for with_routing, and perhaps one more example for using routes.rb. Comments are welcome!


1 Comments (from old blog):

At 12/15/2006 7:26 AM, Rich said…

Thanks for the info. I've got an issue in a related area, and I've been scouring the net and documentation for answers but to no avail. Please help me out if you can!

I'm making a wiki based site, and after the www.mysite.com domain I wanted to have a wiki name before the controller/action/etc... stuff.

So I made an entry in the routes.rb file like this...

 map.connect ':wiki/:controller/:action/:id'

This works a treat when using the app, but when I try to write a functional test for my log in page like this...

 options = {:wiki => "acme", :controller => "account", :action => "log_in"}
  assert_routing "acme/account/log_in", options

  post :log_in, {:wiki => "acme", :controller => "account", :username=>"me", :password=>"me"}

... the assert_routing works, but the post to login comes back with an Exception saying "Need controller and action!"

Any ideas? Cheers, Rich.

Right after I posted Unit Testing: Leave No Trace vs. Schmutz, our team went to the opposite extreme regarding test data: we decided to load all test data (that is, all fixtures) before each test method executed. This took advantage of Rails fixture "feature" of deleting all data from the target fixture's table prior to that fixture's data being loaded into the table. Using this technique, we include all fixtures, thus deleting all Schmutz in the database prior to each test run.

At first, I was against this due to my Leave No Trace Rules philosophy, but then I started thinking about what we, as developers are guaranteeing to the tests, what the "contract" is for them... what is The Promise. I realized that The Promise is not strictly that we will start them with a clean database, but that we will start them from a known starting point. Based on that, deleting-then-loading all data is not really so different from starting from a clean database, because all tests start from the same known state.

I discovered something interesting about Ruby on Rails fixtures. Fixtures, for those who don't know, are how Rails loads test data for unit and functional testing. YAML files are loaded with sample data, which is loaded into your database when each test executes. If you want to load "users" and "addresses" data, you indicate such by stating "fixtures :users, :addresses" in your tests. Pretty handy!

I would expect this data to be cleared from the database at test teardown time, leaving the DB in a pristine state for the next test but this is not the case. Instead, the target fixture's data is deleted, then reloaded at test setup time, while teardown does not clear this data, leaving schmutz -- left over test data.

When it comes to test data, I have always practiced a "leave no trace" philosophy, so my inclination is to disagree with how the Rails folks implemented this: teardown should not leave schmutz.

And before you say anything, transactional fixtures don't fix this. More on that later.

Let's say we're running a test suite consisting of UserTest and UploadTest, each with a bunch of test methods. UserTest loads "fixtures :users, :addresses" while UploadTest loads"fixtures :files". If UserTest executes first, it's last test method will reload users and address, potentially doing a bunch of stuff with those, and then leave it behind. There will be users and addresses polluting the database, potentially interfering with UploadTest, or not, or sometimes, or never or... who knows? Schmutz! Schmutz!

I have heard some arguments for what I will affectionately term the "schmutzy philosophy":

  • If you leave schmutz behind, you can look at the database and see the affects your test had upon the data.
  • If schmutz messes up future tests, it is actually exposing an untested area of the system.
  • In the real world, functionality does not exist in a vacuum, so it's better to test with schmutz around.

And my favorite ...

  • Clearing all the test data is hard. Eventually you load tons of it, and your tests create a bunch more, then they mangle it, and you can't keep track of it all, and so we give up.

None of these arguments are strong enough for me to embrace the schmutzy testing philosophy. Unit testing in isolation has more benefits than any random side effects schmutz might reveal. And as test suites grow and change, schmutz's side affects will be constantly changing, and often muddle the true intent of your unit tests -- I've seen it many times before. Are the features of "save user preferences" truly broken, or is there just a bunch of random junk in the database messing up the results? You won't be able to see the true bugs from the side effect problems.

Ok, now for transactional fixtures. From Clarkware:

Transactional fixtures use database transactions to isolate tests. Rather than deleting and re-inserting fixtures for each test method, transactional fixtures are loaded once at the beginning of the test case. The fixture data in the test database is restored to its original state after each test by doing a transaction rollback.

This still leaves schmutz! It just leaves clean schmutz, which is kind of like clean dirt -- not clean. It seems like transactional fixtures could have solved this problem: perform everything within the transaction: the data inserts, the test's data manipulations, everything, and perform a ROLLBACK in teardown, thus rolling back the inserts as well. Transactional fixtures focus on performance, not on clean tests.

What are your opinions? Does testing with a schmutzy database have merits?


3 Comments (from old blog):

At 1/10/2006 12:01 PM, Chris Nolan.ca said…

I have no problem with it. I don't do anything with the test db except run the tests. Each of my tests have the appropriate fixtures loaded so it doesn't matter to me what is left behind.


At 8/23/2006 5:47 AM, James Mead said…

I totally agree with you, Joseph. In fact this is one of the many reasons we are avoiding the use of fixtures altogether. You might find Mocha gives you another way to approach unit testing.


At 8/31/2006 8:20 AM, Casey Helbling said…

Also be aware of using fixtures, acts_as_enumerated, observers and an enumerated type in an association. You can run into problems when the enumeration fixture data isnt loaded yet but the observer loads the model with the association (trying to use the enum). This is terribly annoying and even loading all fixtures in your test case wont fix it. And to Chris's point, I too load individual fixtures in each test but it is sometimes dificult to know if you add a relationship that depends on a new fixture what other fixtures are required. The arguement could be made that the testing isnt being done at the right granularity but that is different story. Also be aware of problems with rails transactions in general and save points.

http://dev.rubyonrails.org/ticket/5457

Transactions in general in rails do NOT behave as you expect!

Just a quick note on the Ruby on Rails irony: there's hardly any ruby to write. This is great for getting things done quickly, but not so great when people really want to learn ruby. A roving consultant friend of ours has dropped by several times to pair program with us, but he's getting a bit frustrated because there is so little ruby to dive into. "It's all JavaScript and CSS and HTML and all that crap!" he says. Right now we're doing mostly UI overhauls, so that's true! It's amazing how little application logic ruby we've written lately compared to the amount of unit and functional test ruby, and compared to the amount of JavaScript, there's no comparison.

Of course, this a good thing -- when the application is solid, you can spend lots of time implementing UIs that designers create.

While chatting about this over lunch someone noted that, since we are in a phase of not doing much RoR and instead writing lots of JavaScript, we're not on Rails... we're in Jails.