Creating subclasses using Single table Inheritance (STI) - ruby-on-rails

I have the class User, and subclasses Admin and Student.
Student should have additional dedicated columns. Please let me know how I can do this using STI in Ruby on Rails.
Thanks!
Also, how do I populate the users table?

In Rails, typically you'd have type column on the User class. Now in your subclasses you'd inherit from the User class as such:
class User
end
class Admin < User
end
class Student < User
end
This way you can take advantage of the Rails STI and still be able to flexibly create methods for your subclasses.
Find more information here
However to keep it a bit organized, you could put the subclasses in a folder under your models, as such
#models/users/admin.rb
module Users
class Admin < User
end
end
#model/users/student.rb
module Users
class Student < User
end
end
Now to use your classes, you'd do Users::Student.find(id)
UPDATE
In response to the comment, I think for the columns that would be specific to the student, you'd be better served by an association, say Student.has_one :grade or something of sorts, this way you'd have successfully abstracted your user object to deal with the common User methods. But to create a row for Student and Admin
You could do Users::Student.create(params) or Users::Admin.create(params) and Rails knows how to deal with the STI

Related

Rails 4 - Mapping a model dynamically on two different database tables

I have a multi domain app talking to a legacy database.
In that DB I have two tables with different names, lets call them USER_A and USER_B. Their structure and data types are exactly the same, the only difference is that they get their data from different domains.
Now, I would like to have a single scaffold (model/controller/view) that, depending on the domain, maps to the right DB table.
Domain A would work with a model/controller called User which maps internally to the db table USER_A, and Domain B would work with the same model/controller User but maps to the table USER_B.
I would also like to use resource :user in my routes to access the model the rails way.
So somehow I need to overwrite the model on initialization but I am not quite sure how to go about it.
How would one go about this using Rails ActiveRecord?
I don't have a multitable DB ready to test with, so this is an educated guess at the solution:
# respective models
class User < ActiveRecord::Base
end
class DomainAUser < User
self.table_name = "USER_A"
end
class DomainBUser < User
self.table_name = "USER_B"
end
# controller
def set_user
#user = if request.subdomain(0) == "DomainA"
DomainAUser.find(params[:id])
else
DomainBUser.find(params[:id])
end
end
Edit: Here's an alternative bit of metaprogramming hackery which does the subclass instantization within the parent class itself. Tested and working.
I really wouldn't want to maintain something like this though.
# model
class User < ActiveRecord::Base
def self.for_domain(domain_suffix)
class_eval "class DomainUser < User; self.table_name='user_#{domain_suffix}'; end"
"User::DomainUser".constantize
end
end
# controller
User.for_domain("a").new

Ruby on Rails single table inheritance sample form

i'm a super newbie in rails and i need to see a sample code on how to implement Single table iheritance, i have a model called Listing as a super class, and i have subclasses LawFirms and Paralegal, these all extend the Listing model, now i need to be able to create a new listing, but when i am creating i need the form to have an option to select either Law Firm or Paralegal, when Law Firm option is selected, it should show a form for creating a LawFirm object which is different from the Paralegal Object because a law firm has advocates and a paralegal wont have advocates.
So far my models look like this
class Listing < ActiveRecord::Base
end
class LawFirm < Listing
has_many :advocates
end
class Paralegal < Listing
end
How do i create the controller? and the form?
I'm not sure that inheritance is the right solution for this. Inheritance is used for an is-a relationship. For example, a Nissan is a car so Nissan would inherit from the car class. You might be better off having LawFrim or Paralegal as objects in a Listing using a nested resource in rails. You could then add some checks in the controller to make sure it only has one or the other of those objects.

Single Table Inheritance with Conditions

I have model User and model Recruiter. Currently, these are two separate tables, but I want to make them one.
Current:
User: id, username, password, name
Recruiter: id, user_id
Ideal:
User: id, username, password, role (recruiter, admin)
I understand the basics of STI. What I'm wondering is, when I perform methods on the new Recruiter controller (that inherits from User) how do I make sure all my methods are calling on users that are only a recruiter? Thus, queries along the lines of... SELECT * FROM users WHERE role = 'recruiter' for everything.
That is something rails takes care of for you, out of the box. You do not have to manually query on a particular type of user, just query on the right model.
I must also mention that by default rails assumes that your sti_column is called type, but can be overridden to role easily.
Let's admit you have your 2 classes:
class User < ActiveRecord::Base
end
class Recruiter < User
end
Rails will automagically add a type column in the users table so that in your controller, if you do something like this:
class RecruitersController < ApplicationController
def index
#recruiters = Recruiter.all
end
end
Rails will automatically fetch the records with type = 'Recruiter' and you don't even have to set this manually. If you do:
Recruiter.new(name: 'John').save
A new User will be created in database with the field type set to 'Recruiter'.
you would define your models something like this:
class User < ActiveRecord::Base
...
end
class Recruiter < User
...
def initialize
# ... special initialization for recruiters / you could put it here
super
end
...
end
and to create a new recruiter, you would do this:
Recruiter.create(:name => "John Smith")
and because of the type attribute in the STI user table (set to 'Recruiter'), the record will be for a recruiter.
You could put the special initialization for the STI models either in the model's initializer, or in a before filter with a if-cascade checking the type.
An initializer is probably much cleaner.
Have you tried has_one association?
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_one

How to implement associations for multiple roles?

I am implementing a job search application in rails where A user can signup for three different profiles/roles.
Employer
Worker
Enterpreneur(has his own company)
User can choose more than one profile/role.
(I am using the Devise)
My query is that Is it possible to implement the Single Table Inheritance in the above case.?like
CLass Employer < user
CLass worker < user
or what should be the best solutions for database structure to implement the above feature?
Yes it is certainly possible, STI is widely used when models share the same attributes and if you want to be able to query them all together (will help with speed and simplicity since you are loading one db table). You may also share code between the Classes, you only need to put a method once in the parent class and have all the children inherit.
Side note, make sure you are following ruby naming practices, capitalize the classes
Class Worker < User
EDIT (suggested approach)
I would not have these roles inherit from the User, you can still use STI but create another model
Class User < ActiveRecord::Base
has_many :profiles
Class Profile < ActiveRecord::Base
belongs_to :user
Class Employer < Profile
Class Worker < Profile
Class Entrepreneur < Profile
You might want to watch this railscast -- this is what I just recently implemented. It uses a bitmask column in the database. I also paired it with cancan to handle my permissions.

Single table inheritance with relations

I'm trying to build a student portal in Rails 3, but I'm having some problem.
The idea is to have a users table that contains all basic data for a given person. See the UML/E-R below for example attributes.
A user can be both an Assistant and a Student at the same time.
Assistant and Student should inherit from User.
The idea was to inherit directly from the User, like this.
class User < ActiveRecord::Base
# ...
def awesome?
[true, false].sample
end
# ...
end
class Student < User
has_one :student
has_many :registered_courses, through: :students
end
Student.new.awesome?
This makes the relations in the student model very strange.
has_many :registered_courses, through: :students
I want to be able to do something like this in the end.
student.full_name
student.pin_code
student.registered_courses
One solution would be to implementing the method by hand, like this
class Student < User
has_one :student
def pin_number
student.pin_number
end
end
But it looks really strange to refer to a student object inside the student model.
Is there a clearer, better way of doing this?
Here is an example UML/E-R. I've tried to keep this example clean by removing non relevant attributes. That is why there are so few attributes in the registered course entity.
STI is not a good choice for this the way that you have articulated it here, since users can be both students and assistants. When you are using STI, you generally add a type column to specify which subclass the record really belongs to. If both Student and Assistant inherit from User, then that really isn't an option, since you'd be forced to create duplicate User records for someone who is both an Assistant and a Student.
I think you'd be better off simply having Student and Assistant rows that belong_to a Student, and then delegating the elements that are contained in User back to the User object.
I feel like Inheritance is a bad move here. If you're going to have STI like this it HAS to be one or the other.
Instead throw all your logic into the User model, all your data is there anyway. Plus since Student & Assistant aren't mutually exclusive there shouldn't be any methods that will override each other.
Why not STI?
STI is mainly meant for objects that contain the same data, but does different things with them.
For example, I have a specification that contains multiple processes(ex. build and test). So I have a order that contains processes.
process_1:
order_id: 1
specification: foo
type: build
process_2:
order_id: 1
specification: foo
type: test
In this example the only thing that changes in the data is the type, but because the type changes I know what process to perform from the specification.

Resources