I've been having a lot of fun the last couple days doing Behaviour Driven Development (BDD) with test/spec on that new Personal Training online site that I'm coding in Ruby on Rails.
I first setup a User class, modifying the User class generated by acts_as_authenticated.
Then, I added Trainer and Client classes that use a polymorphic association to subclass the User class. It was a little strange, I first was thinking about using Single Table Inheritance (STI), but I figured that the Trainer and Client classes are sufficiently different from each other to warrant their own classes.
If I used STI, I would have had to put all the data for both the Trainer and the Client into the User model. These two roles are quite different, for example, a Trainer would receive a monthly invoice from us, but a Client would receive a monthly invoice from the Trainer. With STI, things could get pretty complicated, so I'm going to try polymorphic associations for now.
To do those polymorphic association for the User, I added a role_id and role_type fields to the model, and :polymorphic to the :belongs_to
# role_id :integer(11)
# role_type :string(255)
class User < ActiveRecord::Base
belongs_to :role, :polymorphic => true
end
Added the "has_one :user" and the "has_many :clients" to the Trainer:
class Trainer < ActiveRecord::Base
has_one :user, :as => :role
has_many :clients
end
and added the "has_one :user" and "belongs_to :trainer" to the Client:
class Client < ActiveRecord::Base
has_one :user, :as => :role
belongs_to :trainer
end
Then, I wrote a whole bunch of specifications to test everything backward and forward. Here's the test for the Client class, the Trainer class is almost identical:
context "" do
fixtures :trainers
fixtures :clients
fixtures :users
setup do
# Trainers
@deryn = trainers(:deryn)
@gordo = trainers(:gordo)
# Clients
@sness = clients(:sness)
@matt = clients(:matt)
@melissa = clients(:melissa)
end
specify "Client and User should be easy to create and link together" do
t = Client.new
u = User.new
u.login = "userlogin"
u.role = t
u.save_without_validation
u.role.should.be t
t.user.should.equal u
end
specify "Client should be able to be associated with a User" do
@matt.user.should.equal users(:matt)
end
specify "Client should be associated with a trainer" do
@matt.trainer.should.equal @deryn
@sness.trainer.should.equal @deryn
@melissa.trainer.should.equal @gordo
end
specify "Should be able to add a Trainer to a Client" do
c = Client.new
c.trainer = @deryn
c.save
@deryn.clients.length.should.equal 3
end
end