Presenting at RailsConf 2008 4

Posted by David Sat, 26 Jan 2008 17:15:59 GMT

I’m going to be presenting at RailsConf 2008 on RSpec’s Story Runner. I’ll be talking about how I approach writing for the Story Runner and address several related issues including:

  • plain text stories vs pure Ruby (when is which appropriate?)
  • extending the RailsStory with custom helpers
  • testing forms using webrat within the Story Runner
  • finding a balance of domain and UI scenarios
  • duplication between stories/scenarios and model/view/controller/helper examples

That’s probably already more than I can cover fairly in a 45 minute presentation, but feel free to make other suggestions if you have them.

See you in Portland!

Presenting at QCon London

Posted by David Wed, 09 Jan 2008 23:29:30 GMT

I’m pleased to report that I’m going to be presenting at QCon London in March. Aslak Hellesøy, Dr. Nic Williams, Kevin Clark and I have been invited to present about various aspects of The Rise of Ruby.

We’ll also be running tutorials on Monday. Aslak and I will present Ruby for Java and C# Programmers and Dr. Nic will present on Rails.

Advanced Rails Recipes 2

Posted by David Fri, 04 Jan 2008 17:04:00 GMT

I’m pleased to announce that my contribution to Mike Clark’s new Advanced Rails Recipes book has been accepted and released. The book is available right now as a Beta PDF and includes dozens of delicious and nutritious recipes for enhancing your Rails applications and the process of developing them.

This is my first formal publication in the software world [1], and I couldn’t be more pleased than to have it be a Pragmatic Bookshelf [2] publication.

My recipe is entitled Describing Behaviour from the Outside-In With RSpec and it demonstrates the BDD approach to Rails apps starting with the view and working your way down to the controllers, models and database.

There is currently one other BDD recipe: Getting Started with BDD, which uses shoulda. There are also recipes for cooking up mocks, code coverage and html validity. And that’s just the testing related recipes.

There are also dozens of recipes dealing with UI, search, email, console, REST, db enhancements and even more general design improvements.

Like the first Rails Recipes book, this one is a must-have for any serious Rails developer who wants to take it up a notch in creating great web applications with Rails.


[1] My first publication of any kind was back around 1980 when I was a young professional magician. The book is called, simply, Coin Magic, and is a must have for any serious coin magician who wants to take it up a notch (see a trend here?) in presenting awesome feats of magic with ordinary coins. Back then I went by my first and middle name, David Arthur.

[2] The Pragmatic Programmers are also publishing my upcoming book with co-author Aslak Hellesøy, tentatively entitled Behaviour Driven Development in Ruby with RSpec.

rspec and autotest 10

Posted by David Tue, 29 May 2007 12:19:00 GMT

At RailsConf 2007 in Portland, I had the privilege of sitting down w/ Ryan Davis and working with him to improve the runtime relationship between RSpec and Autotest. David Goodlad was at the table as well, and helped me work out some of the mappings so autotest runs the right examples when you change application code.

The result is that with RSpec >= 1.0.3 and ZenTest >= 3.6.0, you can now use Autotest with RSpec on your Ruby projects, Rails or otherwise, simply by typing “autotest” in the project root. No additional plugins necessary. Sweet.

One gotcha: Some have reported that when an example fails, autotest keeps running it over and over again until you get it to pass. This is due to a conflict between RSpec’s and autotest’s mechanisms for narrowing down the set of files to run. This is easily resolved by removing the following lines from spec/spec.opts:

--format
failing_examples:previous_failures.txt
--example
previous_failures.txt

If you run ‘script/generate rspec’, these lines will not be included in the generated spec.opts file. Otherwise you can just delete them yourself.

RSpec-0.9 is finally released

Posted by David Tue, 01 May 2007 12:45:36 GMT

We finally released RSpec-0.9 today. We had a little trouble w/ 0.9.0, so we skipped ahead to 0.9.1.

To get the gem:
gem install rspec
To install the Spec::Rails plugin:
ruby script/plugin install svn://rubyforge.org/var/svn/rspec/tags/REL_0_9_1/rspec
ruby script/plugin install svn://rubyforge.org/var/svn/rspec/tags/REL_0_9_1/rspec_on_rails

This release comprises myriad improvements and changes, some of which I’ve blogged about here, the rest of which you can read about on the rspec website.

Of special note is our growing list of contributors. It is very exciting to be involved with a project that has such an active user group filled with imaginative people who have contributed some of RSpec’s most exciting new features.

Thank you, thank you, thank you to all who participate whether it is through patches, feature requests, bug reports, or simply interacting on the mailing lists. You’ve all had an important impact on RSpec’s growth.

RSpec-0.9.1 and Autotest (ZenTest-3.5.2) 6

Posted by David Tue, 01 May 2007 12:27:00 GMT

Autotest (part of ZenTest) now supports RSpec. This is fantastic news! For those of you who do not know about autotest, it is a program that runs in the background while you are writing your tests and code. Each time you make a change it automatically reruns your tests – and now your specs, too! This is a powerful addition to the TDD/BDD experience.

Recent releases of both tools overlapped a bit so there are changes in RSpec-0.9.1 that are not reflected yet in ZenTest. Also, while ZenTest-3.5.2 supports Spec::Rails, RSpec’s Ruby on Rails plugin, it does not support non-Rails Ruby projects.

I’ve submitted a patch to the ZenTest project which addresses both of these issues. Until the patch is applied, or the issues are addressed in some other way, you can apply it yourself to get autotest working with RSpec for Rails and other projects. These steps work on a mac. I assume that the commands are quite similar for Linux and Cygwin users.

  1. Go to http://rubyforge.org/frs/?group_id=419 and download ZenTest-3.5.2.tgz
  2. Unpack the tar and
    tar zxvf ZenTest-3.5.2.tgz
    cd ZenTest-3.5.2
    
  3. Get and install the patch
    curl -O http://blog.davidchelimsky.net/files/ZenTest-3.5.2-rspec.patch
    patch -p0 < ZenTest-3.5.2-rspec.patch
    
  4. Build and install the gem
    rake gem
    sudo gem install pkg/ZenTest-3.5.2.gem
    

Once you’ve built and installed the patched gem, you run autotest as normal. Stand in the root of your project and say:

autotest

If you have a spec directory at the root of your project, autotest will load up rspec_rails_autotest for Rails projects and rspec_autotest for everything else.

To quote Josh Knowles, Happy (Auto)Specing!

predicate_matchers 3

Posted by David Tue, 01 May 2007 00:00:00 GMT

Updated on 5/2/2007

In RSpec-0.8 if you say …

File.should_exist(path)

... the expectation passes if File.exist?(path). Here’s how that should look in RSpec-0.9, with the underscore removed:

File.should exist(path)

Supporting this for any arbitrary predicate would require more method_missing magic than we were willing to stomach, so we added a means of easily declaring methods like this yourself. We’ve supplied #exist out of the box, but you can add your own with a simple declaration.

Here’s how you do this for an individual behaviour:

describe Fish do
  predicate_matchers[:swim] = :can_swim?
  it "should swim" do
    Fish.new.should swim
  end
end

And here’s how you define them globally, so they are available in every example in your suite:

Spec::Runner.configure do |config|
  config.predicate_matchers[:swim] = :can_swim?
end

rspec.should use_a_little_less_magic 6

Posted by David Wed, 10 Jan 2007 11:12:00 GMT

Updated on 21 Jan

One of the great things about Ruby is that it allows common folk like you and me to perform all sorts of magic that would dazzle and amaze the more statically typed of our neighbors.

One of the terrible things about Ruby is that, as the secret gets out, the various magic potions we mix begin to conflict with each other, creating the foul stench of bugs that don’t reveal themselves until someone ELSE does some of their own magic.

The most visible conflict has been between RSpec and Ruby on Rails, both of which usurp method_missing to support some devilishly sexy run-time method creation. The problem comes when RoR follows good guidelines like late-binding, and adds its version of method_missing later in the stack than RSpec does, rendering RSpec’s use of method_missing a bit futile, forcing RSpec to monkey patch Rails in order to work.

Well, this conflict may soon see its end.

We’ve just added support for a new way of setting expectations in RSpec that should support all of the existing expectations AND make it very easy to add custom expectations with no method_missing magic or monkey patching. Thanks to Dan North for this )&)(&ing brilliant idea.

Here’s how it looks:


result.should equal(3)
light.should be_red
newspaper.should be_read
rspec.should use_a_little_less_magic

And here’s how simple it is to add a custom expectation:


module HotelExpecations
  class BeBookedSolidOn
    def initialize(date)
      @date = date
    end

    def matches?(hotel)
      hotel.booked_solid_on?(@date)
    end

    def failure_message
      "expected hotel to be booked solid on #{@date}" 
    end

    def negative_failure_message
      "expected hotel to not be booked solid on #{@date}" 
    end
  end

  def be_booked_solid_on(date)
    BeBookedSolidOn.new(date)
  end
end

context "Hotel behaviour" do
  include HotelExpectations

  specify "should be considered booked solid with 100 reservations" do
    #given
    @hotel = Hotel.new

    #when
    (1..100).each { @hotel.reserve_room_for(2).on("12/31/2007")

    #then
    @hotel.should be_booked_solid_on("12/31/2007")
  end

  specify "should NOT be considered booked solid with 99 reservations" do
    #given
    @hotel = Hotel.new

    #when
    (1..99).each { @hotel.reserve_room_for(2).on("12/31/2007")

    #then
    @hotel.should_not be_booked_solid_on("12/31/2007")
  end
end

How easy is that??!

As of this writing, this is only available in the trunk, however it will be quietly released in 0.7.6, and perhaps with a bit more fanfare in 0.8. Those of you who make a habit of using the latest and greatest can do this RIGHT NOW, though beware that until the fanfare is sounded, the details may change a bit.

Fighting the Urge to Ask 2

Posted by David Mon, 27 Nov 2006 20:19:00 GMT

ActiveRecord provides a lot of magic methods that let us get at the properties of a given AR subclass. This is absolutely fantastic news!

Except for one thing.

Because the resulting getters are public, the entire world of Rails applications (including those that I have written) is infested with violations of encapsulation, TDA (Tell, Don’t Ask), Feature Envy and Inappropriate Intimacy. Wow. That’s some stinky &^#(.

So what can be done about this? We don’t want to encourage the Rails core team to make all the getters private for two reasons. One, there are so many apps in existence already that would break, the entire community would rightfully have my head for even joking about it! Two, it’s not THEIR responsibility. It’s OURS.

Think of Ruby and the awesome power that we’ve been granted by open classes. If you want to screw up the coding world you live in by abusing this ability, then have at it. We’ve done that in RSpec, and are learning the lessons from having done so (don’t invade other people’s classes so much in a framework!).

The responsibility is ours, as users of the Rails framework, to use it in responsible ways, adhering to the principles of Object Oriented Design that have guided us through the morass of java and .NET applications that have come before.

One example comes from a recent discussion on the RSpec list. The names here have been changed to protect the innocent. Imagine you’re writing an app for a Veterinarian’s office in which each Pet belongs to a Person. When you’re registering a new Pet you want to be able to create the Pet and the Person (pet owner) in one action. So you might have something like this:


def create
  @person = Person.new(params[:person])
  @pet = Pet.new(params[:pet])
  @person.pets << @pet
  @person.save
end

Anybody see the violation of TDA? Anybody smell any Feature Envy? For those who don’t, the problem is in this line:


  @person.pets << @pet

The problem is that this controller code now depends on the internal structure of the Person – the fact that it keeps a collection of Pets. Now imagine that as this app evolved, we realized that we had the relationship backwards. That, since we are a Pet Health Clinic, after all, everything should revolve around the animal, not the owner. This code (and all the other code in the application like it) will have to change.

We can avoid that change, or any other change to how the Person and Pet relate to each other, by adding an add_pet method to Person:


  @person.add_pet @pet

Now, if we decided to reverse the relationship, we could do by having the implementation of add_pet to turn around and add the Person to the Pet:


  class Person < ActiveRecord::Base
    def add_pet pet
      pet.add_person(self)
    end
  end

Now you could argue that the code that deals w/ People and Pets should change to correctly reflect the new model. But by applying Tell, Don’t Ask in this case, we can decide when to make that change based on priorities, not based on urgency.

So I urge you to pay attention to these little traps that AR provides for us, and fight the urge to Ask. Just Tell the Other Guy. It’ll save your ass some day.

Cohesive Models 3

Posted by David Wed, 15 Nov 2006 12:53:00 GMT

Now that Rails uses Ruby to express database structure (migrations), it seems silly to me that we can’t just express that right in the model classes. Something like this:


class Person < ActiveRecord::Base
  fields do |f|
    f.add :first_name, :string
    f.add :last_name, :string
  end
end

Doing this would not only make models more cohesive, but it would make it easier to specify a lot about models without ever touching the database.

We’d need a means of discovering necessary migrations on the fly, and that could get very tricky very quickly, but I think it’s worth exploring.