Enforce mutual exclusivity

Hi,

First post, sorry if this is the wrong place or has been answered
before.

I’m using ActiveRecord and I want to enforce mutual exclusivity on a
has_and_belongs_to_many.

A concrete example: I have a User which can have one or more Roles
(student, tutor, headmaster, administrator etc). However if a user is a
student they cannot hold any other role. I was hoping to find something
like validate_exclusive but that doesn’t appear to exist. Can anyone
suggest how I can implement this. Otherwise, is there a better pattern
I could use to model this?

Many thanks,

Toby.

On Sep 7, 2007, at 4:59 PM, Toby O’Rourke wrote:

is a student they cannot hold any other role. I was hoping to find
something like validate_exclusive but that doesn’t appear to exist.
Can anyone suggest how I can implement this. Otherwise, is there a
better pattern I could use to model this?

Many thanks,

Toby.

It’s a design pattern that occurs frequently with CRUD applications,
in Rails or anywhere else.
I think of this pattern as the “category” pattern.
Basically you want an ‘option list’ (in xhtml ) and to generate that
in Ruby/Rails you have tons of options.
Creating an option list /drop down list is covered well in AWDWR.

One way is to hard code it:
You can define a constant array or hash in your model file to provide
a list from.
(not all models have to be in the database)

Another way is to store it (more flexible, search and sort
capabilities are closer to ready to go):
You could also simply create a model and subsequent migration and db
table that contains the categories for this.

Then have your User model belongs_to category (singular!) and Users
have a category_id, integer column in the db table.

Since you’re using a drop down list in the interface, there will only
be one choice.
You could even make it more complicated with authentication
preventing certain users from being in certain categories, but at
that point things get messy and you might want to carefully review
the design.

If you want to just do it with validation, you can write your own
validates_as
In it, you just need a list of validations (think about
capitalization, since it does matter) and you can use exclusive
(a.k.a. XOR) or logic to match them. You could also try a case
statement, but validations might barf on that.
If you use the exclusive or approach, you’ll need to repeat the list
of valid options in the error message of your validates_as_something

This is more of a Rails question, because it focuses on Rails models.
But in them, you can use a lot of Ruby.
You should get AWDWR or Ruby for Rails or Rails Cookbook.

The solutions can be more or less Rubyish, but all revolve around Rails.
You could try the Rails Talk list, but you might not get as good of
an answer there for this question as you might find in a good book.

On 9/7/07, Toby O’Rourke [email protected] wrote:

Hi,

First post, sorry if this is the wrong place or has been answered before.

I’m using ActiveRecord and I want to enforce mutual exclusivity on a
has_and_belongs_to_many.

A concrete example: I have a User which can have one or more Roles
(student, tutor, headmaster, administrator etc). However if a user is a
student they cannot hold any other role.

Despite the knee jerk reaction to direct you to the rails mailing
list, the real place to ask this question would be some group / list
dedicated to data modelling in RDBMSs. That said I think it’s an
interesting question so I’m going to try and answer it :slight_smile:

If student is the only that is mutual exclusive with any other role,
then one way to look at it is that student is not actually a role. One
way to do this is to pull out all the common information about a
person (name, address, phone number, what have you) and stick that
into a person_info relation. You then an have two relations, students,
and nonstudents. Both of these would simply have a field refererring
to the primary key of the person_info relation. Then your relation for
the roles would pair roles with nonstudents.

This has problems of course. If student isn’t the only exclusive role,
or if the rules are more complicated (like a headmaster can’t be a
tutor but they are an administrator) this kind of arrangement will
fall apart pretty quickly.

Another option is to divide the roles into N groups of mutually
exclusive roles, and have N fields in your relation. The problem with
this is that you might not be able to divide your roles like this. It
is also, like my first suggestion, brittle with respect to changes in
the rules of what roles are mutually exclusive.

Yet another option is to enumerate all legal role combos, and
associate each combo with an id. Then to assign a role or a set of
roles you just use the id.