Forum: Ruby on Rails Class can be many other..classes

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Stephen B. (Guest)
on 2007-05-12 23:31
(Received via mailing list)
Hey All,

Before I go off on a tangent, I thought it best to see if anyone has a
pattern for this already.

An example should explain all:

Let's say I have a Person class and a person can be both a Teacher and
a Performer.  I feel that Teacher and Performer are classes in their
own right as they have other things associated with them.  Including
all attributes and associations under the User class will get real
messy, real quick.

At first I experimented with STI but obviously a class can only be one
type.

I could also use standard associations and have this kind of set up:

person = Person.find(1)
person.teacher.level
person.performer.styles

This could prove to be the most straight-foward way of doing it, but I
can't help feeling that the definitions aren't representative of the
domain - and that kinda bothers me.

I feel like saying "a Person can be a Teacher and/or a Performer"...

Anyway, I'd be very interested in anyone's thoughts on this.  If there
is a pattern out there for this that would apply to ActiveRecord then
I'm poised to code it up.

Cheers,

Steve
Steve R. (Guest)
on 2007-05-12 23:38
(Received via mailing list)
I don't think inheritance/polymorphism solve your problem. You might
look to roles (has_many :roles, :through => :join_table).
Stephen B. (Guest)
on 2007-05-12 23:48
(Received via mailing list)
> I don't think inheritance/polymorphism solve your problem.
I agree.

> look to roles (has_many :roles, :through => :join_table).
Hmm.. roles do sound more promising...  I think that inheritence could
be used then.  If I say that Teachers and Performers are Roles that
People have, it feels a lot more natural.

See? It's always best to ask.  You should have seen some of the ideas
I was having for what's actually a straight forward problem...

Cheers,

Steve
Alain R. (Guest)
on 2007-05-12 23:49
(Received via mailing list)
Steve

  > Let's say I have a Person class and a person can be
  > both a Teacher and a Performer.

I'm sure there are better ways but what about : (untested)

    class Person <  ActiveRecord::Base
      has_many :classes
      has_many :performances

      def teacher?   ; classes     .empty?    end
      def performer? ; performances.empty?    end
    end


Alain R.
--------
http://blog.ravet.com
Stephen B. (Guest)
on 2007-05-12 23:56
(Received via mailing list)
Alain,

Yeah that's a nice idea but my main concern is that as the application
grows, it will get messy with this kind of set up.  For example, if a
Person could be a Student I'd need to add yet more relationships and
attributes.

The Roles idea suggested by s.ross will allow me to organise my
classes neatly and there's room for expansion as the application
grows.

Cheers,

Steve
Alain R. (Guest)
on 2007-05-13 00:07
(Received via mailing list)
Steve,

  > Yeah that's a nice idea but my main concern is that as the
application
  > grows, it will get messy with this kind of set up.  For example, if
a
  > Person could be a Student I'd need to add yet more relationships and
  > attributes.

Personally I prefer the compact style of my first post, but I guess
you could "modularize"/"role-arize" it :
(untested)

    class Person <  ActiveRecord::Base
        include Teacher,  Performer, Student
    end

    module Teacher
      def self.included(base)
        base.has_many :classes, :dependent => :destroy
      end
      def teacher? ; !classes.empty? end
    end

    module Student ..

    module Performer ..


Even with 3 roles I still prefer the compactness of :

    class Person <  ActiveRecord::Base
       has_many :classes     , :dependent => :destroy
       has_many :performances, :dependent => :destroy
       has_many :enrolments  , :dependent => :destroy

      def teacher?   ; !classes     .empty? end
      def performer? ; !performances.empty? end
      def student?   ; !enrolments  .empty? end
    end

Alain R.
--------
http://blog.ravet.com
Stephen B. (Guest)
on 2007-05-13 00:28
(Received via mailing list)
Actually, you know what?  I think you may be right.  I just started
explaining why I thought the multiple model idea was better and
realised that it was actually no better than my original idea of using
relationships.

I want to avoid this:

person.teacher.regular_classes

So your idea of putting all the relationships in the Person class
achieves that.  I originally thought that I wanted to be able to do
this:

Teacher.find(:all)

but to be honest, I think I might be better off having 'teacher',
'student', 'performer' attributes in my people table and use those.
Seeing as a Person could be all 3 roles there's nothing messy about
having the attributes and associations in the same class.

Cheers,

Steve
Alain R. (Guest)
on 2007-05-13 00:46
(Received via mailing list)
Steve,

   > I originally thought that I wanted to be able to do this:
   > Teacher.find(:all)


The simplest shortest (but slowest) way that works :

  teachers = Person.find(:all).select(&:teacher?)

(Don't use if you have more than xxx people in your DB)


Alain R.
--------
http://blog.ravet.com
Steve R. (Guest)
on 2007-05-13 01:10
(Received via mailing list)
If you know that the only three roles a person can assume are
teacher, student, and performer, then you're in good shape with this
solution. It's when you get a few months down the road and you have
to add hall_monitor or class_clown that you begin to have issues with
hard-typing instead of relational (role-based).
This topic is locked and can not be replied to.