MarkUs Blog

MarkUs Developers Blog About Their Project

Rebooting the Schema (part 2)

with 2 comments

Last time, I talked about the old model schema, and the problems it had that lead us to refactor the code. After refactoring, this is what the database looks like:


The relationships are now more concrete with the addition of memberships and assignments_groups tables. The assignments_groups is a Rails convention of declaring a many-to-many relationship between two objects by use of the join table. Thus, an assignment can have many groups, and groups can also have many assignments if a group persists throughout the course. A caveat though is to make sure that the join table is in alphabetical order, meaning it must be assignments_groups and not groups_assignments. That’s just the “convention-over-configuration” mantra of Rails at work.

Once we have the database schema set, we can then just go in and declare those relationships in the ActiveRecord classes respectively:

class Group < ActiveRecord::Base
has_and belongs_to_many :assignments
class Assignment < ActiveRecord::Base
has_and belongs_to_many :groups

However it is a different case if the join table contains extra information, which is our case with the memberships table. Here, not only does it reference the user and the group together, but it also contains extra information such as status of the member. Thus, we need to have a Membership class representing a member, and use has-many-through relationship. which sort of explicitly states that the association between a User and a Group uses memberships as its link. Here we declare the relationship as follows:

class Membership < ActiveRecord::Base
belongs_to :user
belongs_to :group
class User < ActiveRecord::Base
has_many :memberships
has_many :groups, :through => :memberships
class Group < ActiveRecord::Base
has_many :memberships
has_many :members, :through => :memberships, :source => :user


We’ve also separated the old submissions table to a submissions and submission_files tables. The new submissions table doesn’t seem to have much information and seems to be a waste of space. However, having this table allows us to delegate submission functions to a Submissions class rather than mixing them directly with either the User or Group classes. All we have to do now is just ask a User or Group for its Submission instance, and handle all queries related to submitted files from it.

Since we also want to avoid checking to see if it is a User or a Group submission everytime, we’ve abstracted the Submissions class and added separate classes for each type, UserSubmission and GroupSubmission – classes that are linked to Users and Groups respectively. Since instead of declaring the relationship with Submissions, we have:

class User < ActiveRecord::Base
has_many :submissions, :classname => UserSubmission
class Group < ActiveRecord::Base
has_many :submissions, :classname => GroupSubmission

class UserSubmission < Submission
has_many :users
class Group
Submission < Submission
has_many :groups

This allows us to call either user.submissions or group.submissions and return with an instance of the appropriate Submission subclass type.

Final Results

The refactored models with the appropriate associations gave way to a much cleaner code in the end. With the schema set in place, I’ve revisited the old code and heeded the advice in the first post, stuffing all the business logic in the appropriate models and leaving workflow control to the controllers. The result turned several functions with 200+ lines into a single function with less than 50 lines. I was also able to create more thorough unit testing while code was being written. Here, we can see that we’ve improved our stats quite a bit:

$ for f in app/controllers app/models app/helpers; do echo $f
`find $f -name "*.rb" |xargs wc -l |tail -n1`; done
app/controllers 563
app/models 591
app/helpers 60

In retrospect, I think the refactoring decisions suits us very well with what we have in mind and gives us room for modifications at the same time…until we actually start porting OLM. Stay tuned.

Written by Geofrey

October 14th, 2008 at 6:32 pm

2 Responses to 'Rebooting the Schema (part 2)'

Subscribe to comments with RSS or TrackBack to 'Rebooting the Schema (part 2)'.

  1. […] the next post, I’ll talk about what the new schema looks like and what improvements we’ve made in […]

  2. […] Currently, checkmark supports the simple case where an assignment has groups of a specified size. These groups can be made by the instructor manually or through a CSV upload (which is incompletely implemented – a little bit more on this later) and also by the students themselves. Basic group creation currently works with the current schema: (Which does not include the rubric or annotation stuff) which is explained by Geofrey here. […]

Leave a Reply