Changing types in single inheritance

I'm trying to write an application that involves with three kinds of inherited models:

The lowest is Person, a model merely used as records, and nothing else of importance. Officer inherits from Person, and unlike Person, stores a password for verification and logging in. Admin inherits from Officer, and is the same as Officers except there must be at least one of them.

I want to make an internal method that allows me to turn a Person into an Officer, an Officer to Admin, Admin to Person, etc. What's a clean method of upgrading/downgrading within this single-inheritance?

Here's the source file, as well as a quick template for what I want to see happen: =======Person.rb======== class Person < ActiveRecord::Base   #first_name is required: must start with a capital   validates_presence_of :first_name   validates_format_of :first_name,     :with => /^[A-Z][a-zA-Z0-9, .]+$/

  #last_name is required: must start with a capital   validates_presence_of :last_name   validates_format_of :last_name,     :with => /^[A-Z][a-zA-Z0-9, .]+$/

  #rin is required: must be unique; also, must all be lowercases   #with 0 to 2 numbers following the letters.   validates_presence_of :rin   validates_format_of :rin,     :with => /^[a-z]+[0-9]{0,2}$/   validates_uniqueness_of :rin

  #email must be unique, and it is required.   validates_presence_of :email   validates_format_of :email,   :with => /^[a-zA-Z0-9_]+@[a-zA-Z0-9_]+(\.[a-z]{2,3}){1,2}$/   validates_uniqueness_of :email

  #year is required, and must be a 4 digit number greater than 2000   validates_presence_of :year   validates_numericality_of :year,     :only_integer => true   validates_length_of :year,     :is => 4

  #turns a Person or Officer to Admin   def turn_to_admin     self[:type] = 'Admin'     #somehow update with Admin-based validations     self   end

  #turns a Person or Admin to Officer   def turn_to_officer     self[:type] = 'Officer'     #somehow update with Officer-based validations     self   end

  #technically, this does nothing   def turn_to_person     nil   end

  protected

  def validate     errors.add(:year, "should be between 2000 and 3000") if       ( year.to_i < 2000 or year.to_i > 3000 )   end

  #completely ignore the passwords end =======Officer.rb======== class Officer < Person #take care of password thingy   validates_length_of :password, :minimum => 5

  attr_accessor :password_confirmation   validates_confirmation_of :password

#some functions   def validate     errors.add_to_base("Missing password") if hashed_password.blank?   end

  def self.authenticate(name, password)     person = self.find_by_rin(name)     if person       expected_password = encrypted_password(password, person.salt)       if person.hashed_password!=expected_password         person = nil       end     end     person   end

  def password     @password   end

  def password=(pwd)     @password = pwd     create_new_salt     self.hashed_password = Officer.encrypted_password(self.password, self.salt)   end

  #turn_to_admin is inherited from Person

  #This does nothing   def turn_to_officer     nil   end

  #turns an Officer or Admin   def turn_to_person     self[:type] = 'Person'     #somehow update with Officer-based validations     self   end

#careful of private statements   private

  def self.encrypted_password(password, salt)     string_to_hash = password + "Japan is an island of interest" + salt     Digest::SHA1.hexdigest(string_to_hash)   end

  def create_new_salt     self.salt = (self.object_id * rand).to_s + rand.to_s   end end =======Admin.rb======== class Admin < Officer

#Make sure there's at least one admin   def after_destroy     if Admin.count.zero?       raise "Can't delete the last admin"     end   end

  #turn_to_person is inherited by Officer

  #Return nil, again   def turn_to_admin     nil   end

  #turns an Admin to Officer   def turn_to_officer     self[:type] = 'Officer'     #somehow update with Officer-based validations     self   end end

Single table inheritance applies perfectly to this need, basically you have to add a type column, and build the people table with all the columns that any of the three classes will do, then both admin and officer have to inherit from person, (I never tested double inheritance) so some repetition at admin and officer model may happen, (you can use a mixin if you want to avoid that)

I think he’s got this already.

What he’s trying to do is assign a person to be an Admin or an Officer. I think all you do is self.type = "Admin and then self.reload and it might work.

Yeah, I did already know. It's is why I was asking something else :-)! Anyway, I tried the reload method:

Reinitialize the variable seems to be the only option then, switch the type over to Officer and then do Officer.find(@person.id) and it may work that way.

Behold!!!

Your test is wrong. You should be doing:

person = Person.find(id) person.type = “Officer” person.save officer = Officer.find(id) officer.class == Officer

And that will return true.

Oh, whoops.

Anyway, it did work. I'm curious about why "person.type = " works. I read somewhere that the correct syntax should be "person[:type] = ", due to the fact that ".type" is a (deprecated) class method.