Ruby on Rails is a celebrated recent phenomenon, and like many others, I have been seduced by its ease, and simplicity. One of the things that I have found with its MVC style approach, is that you really need to have a good grip on the design of your data requirements. At the heart of this is ActiveRecord which is the Model.

Inorder to understand ActiveRecord more, I played around with using it outside of Rails ( I know that you can use the console ), to see what you can do. Seeing that I had a hard time finding much information about this (more specifically - examples), I have written up a small example that I worked through to demonstrate the way ActiveRecord encapsulates a "many to many" relationship.

I started with two entities - Users and Roles, and wished to describe:

  • A User has one or many Roles
  • A Role can belong to one or many Users

Firstly - define the tables - ActiveRecord takes the approach of defining a table per entitiy, and then a separate table to hold relationship between the entities, and as with all things Rails, it relies heavily on naming convention to knit it together.
Users => users
Roles => roles
Roles <=> User => roles_users

CREATE TABLE roles (
  id int(10) unsigned NOT NULL auto_increment,
  name varchar(50) NOT NULL default '',
  info varchar(50) default NULL,
  PRIMARY KEY  (id)
) TYPE=MyISAM;

INSERT INTO roles VALUES (1,'role2','some role 2 info');
INSERT INTO roles VALUES (2,'role1','some role 1 info');

CREATE TABLE roles_users (
  role_id int(10) unsigned NOT NULL default '0',
  user_id int(10) unsigned NOT NULL default '0',
  KEY ur_map_idx (role_id,user_id)
) TYPE=MyISAM;

INSERT INTO roles_users VALUES (1,1);
INSERT INTO roles_users VALUES (2,1);

CREATE TABLE users (
  id int(10) unsigned NOT NULL auto_increment,
  nick varchar(50) NOT NULL default '',
  name varchar(50) default NULL,
  password varchar(50) NOT NULL default '',
  modified timestamp(14) NOT NULL,
  created timestamp(14) NOT NULL,
  access timestamp(14) NOT NULL,
  PRIMARY KEY  (id)
) TYPE=MyISAM;

INSERT INTO users VALUES (1,'pxh','Piers Harding','blah',20050512121552,20050512121552,20050512121552);

Next thing is to create the Model, and the code to access it. As this is installed using Ruby Gems, you must import gems, and then use gems to bootstrap the loading of ActiveRecord.

#!/usr/bin/ruby
# load gems, and then ActiveRecord
require 'rubygems'
require_gem 'activerecord'

# Establish a conneciton to your database
  ActiveRecord::Base.establish_connection(
      :adapter  => "mysql",
      :host     => "badger",
      :username => "root",
      :password => "",
      :database => "auths"
   )

# Define the entity Role
class Role < ActiveRecord::Base
# describe the relationship to users
  has_and_belongs_to_many :users
end

# same for users
class User < ActiveRecord::Base
  has_and_belongs_to_many :roles
end

# get the first user
user = User.find(1)

# find the roles for a user
roles = user.roles

# loop through each role
roles.each {|role|
             print "Role: #{role.info}\n"
# find users for the role
             users = role.users
             users.each {|u|
# and again find the roles for each user
                userroles = u.roles
                print "Roles for user #{u.name} \n"
                userroles.each{|r| print "\t\t -->role: #{r.name}\n"}
                }
          }

Hope this helps someone.

Posted by PiersHarding at June 16, 2005 12:33 PM