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