Repository Pattern: Day 2
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.