How to keep tables uncluttered by using profiles for each user type in Ruby on Rails

This is a continuation of my STI / Single Table Inheritance article: “How to use multiple User types with the same model in Rails aka STI or Single Table Inheritance

To keep your tables less cluttered and better normalized, I do recommend creating profile tables for each user type. What I did is this for my student’s example:

I have a student_profiles table with things like skype_username, birthdate, etc. To set this up You’ll want to do your migration.

rails g migration create_student_profiles birthdate:date skype:string instrument:string

Then we edit the Student model like so:

class Student < User
  authenticates_with_sorcery!

  # lets setup the relationship, name it profile for ease, the foreign key on the student_profiles table relating back to the db is user_id, and we setup an inverse relationship so we can reverse query, and dependent destroy so if we destroy the user, we also destroy the profile. 

  has_one :profile, class_name: 'StudentProfile', foreign_key: :user_id, inverse_of: :student, dependent: :destroy

  # In my form, I have update the student AND profile with the same form using a nested fields_for call, to do that we need this little tidbit:  
  accepts_nested_attributes_for :profile, :instruments, :students_instruments

  # This ensures we have a profile to manage, when the student is setup or initialized. 
  after_initialize do 
    self.profile ||= self.build_profile
  end

  # This is a method I have setup so I can simply do: Student.birthdate instead of Student.profile.birthdate ... it basically tries to get an attribute from the profile, if the attribute itself doesn't exist. 

  def method_missing(m, *args, &block)
    self.profile.read_attribute("#{m}") 
  end
end

Now you can keep your user table and class more minimal and separate everything out into their own model. I also recommend putting all of these folders in models/users folder. To do that though you will need to edit config/application.rb and add the following line:

config.autoload_paths += %W(#{config.root}/app/models/users)

Thinking ahead you may want to group more models together, especially if you have a large number of models and tables, to do that you’ll just need to add a new line the same thing as above, just change the last part of the path to match the path to the folder with your models, and you should be good to go!