Today I dove back into setting up my models and datastores with DataMapper and Postgres and although the going was slow I had some moderate success. The slow-going is due to the fact that I’m using some entirely new concepts and tools, but the pace is gradually picking up as I get more comfortable.

The first new thing (for me) that I’m implemented was RSpec’s shared_examples. Since I have a working, tested app using in-memory repository, in theory I should be able to just use those tests to insure that my DataMapper objects and Postgres repository behave the same way.

In practice it’s not too far off, but there are still a lot of little “gotchas” that I’m having to sort out in order to use the same tests and get the same behavior. One of the big ones was namespacing. I have two user classes to represent the different models, one for DataMapper and one for an in-memory User, which means two user.rb files too. In order to make sure that the specs are calling the right one I’ve namespaced the in-memory one into a module named “Memory”. Then my Datamapper User model exists at the top-level. This means that whenever I call User.somemethod I will get my DataMapper model (and I can even put ::User.somemethod to be completely sure I’m at the top level), and if I want to use my in-memory User I have to call Memory::User.some_method.

Here’s abridged versions of what the two user files look like:

#in file lib/datamapper_datastore/models/user.rb

require 'data_mapper'
class User
  include DataMapper::Resource

  property :id,         Serial
  property :name,       String
  property :email,      String
end

..

#in file lib/memory_datastore/models/user.rb

module Memory
  class User
    attr_accessor :id, :name, :email
  end
end

And here’s how I’m implementing the shared examples in my RSpec tests.

#in file spec/datamapper_datastore/models/user.rb

require 'spec_helper'
require 'lib/dm_datastore/models/user'
require 'lib/shared_examples/models/user_examples'

describe "DataMapper user" do
  before(:all) do
    @user = User.new(:name => "Mike", :email => "test@test.com")
  end

  it_behaves_like "user model"
end 

..

#in file spec/memory_datastore/models/user.rb

require 'spec_helper'
require 'lib/memory_datastore/models/user'
require 'lib/shared_examples/models/user_examples'

module Memory
  describe "In-Memory User" do
    before(:all) do
      @user = User.new(:name => "Mike", :email => "test@test.com")
    end

    it_behaves_like "user model"
  end
end

And then my shared examples file for the models just loooks like this.

#in file spec/shared_examples/models/user_examples.rb

shared_examples "user model" do
  it "should do something" do
    ...some code...
  end

  context "User password and authentication" do
    it "should set a password" do
      ...some code...
    end

    ...some more tests....
  end
end

Now whenever I run my specs all of the tests from shared_examples/user_examples are run for both the DataMappper and in-memory user objects.

The next step (once I get all of this set-up) will be to set up a separate rake task to run the specs against the DataMapper objects so that they aren’t called every time I run rspec spec. As I mentioned yesterday, I was running 100 tests in a .1 seconds so it was easy to constantly fire them off. After adding in only seven DataMapper tests I’ve added almost a second to the test suite, and it’s funny how jarring that increase in time is. What was an instant flow of green (with some red and yellow, of course) is now a stuttering line. As I continue with the shared_examples I’ll be adding another 70 tests to various DataMapper objects, which means approximately 10 seconds of tests. 11 seconds of tests won’t be terrible, but quarantining those tests off into a separate task will be a lot nicer.