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