The other option you have to STI
is enum
:
#app/models/profile.rb
class Profile < ActiveRecord::Base
enum state: [:provider, :seeker, :professional]
end
This gives you an int
column (in this case state
) which denotes whether the object has a particular property (EG provider?
/ seeker?
etc).
It would provide a similar set of functionality to STI
, except giving you a single model to call (rather than the 3
you'd get if using an STI pattern).
STI
STI's are good if you have the need to call multiple models.
Most of the time, you don't. There's a good writeup about it here.
If using STI
with your case, you'd end up with:
#app/models/profile.rb
class Profile < ActiveRecord::Base
belongs_to :user
end
#app/models/seeker.rb
class Seeker < Profile
end
#app/models/professional.rb
class Professional < Profile
def add_client
...
end
end
The important thing to note is that although this looks pretty in your models, it means your front-end will need to call:
#config/routes.rb
resources :professionals, only: [:new, :create]
#app/controllers/professionals_controller.rb
class ProfessionalsController < ApplicationController
def index
@professional = Professional.find params[:id]
end
end
If you're planning on calling Profile.find_by type: "professional"
, forget it. That's an antipattern & highly inefficient.
--
The way to determine whether you really need to follow the STI pattern is very simple -- do you need extra methods / attributes for each subclass?
If not, then you can get away with an enum:
#app/models/user.rb
class User < ActiveRecord::Base
has_one :profile
before_create :build_profile #-> creates blank profile with each new user
accepts_nested_attributes_for :profile
end
#app/models/profile.rb
class Profile < ActiveRecord::Base
belongs_to :user
enum state: [:provider, :seeker, :professional] #-> defaults to "provider"
end
I would personally use an enum
with conditioning in the controller:
#app/controllers/profiles_controller.rb
class ProfilesController < ApplicationController
def edit
@profile = current_user.profile
if @profile.professional?
...
elsif @profile.seeker?
...
end
end
end
--
The best way to understand how this works is to look up about object orientated programming, which is Ruby/Rails in a nutshell.
OOP uses classes to create "objects"...

Each of these objects are invoked as instances (which is where the terms "instance variable" and "instance method" come from) - these instances being held in memory by your application.
The way OOP programs work is by taking inputs from the user, determining the interaction between the objects, and outputting the result. This is how all modern games work.
Thus, when you look at Rails, you have to look at it from the perspective of which objects you're going to be invoking. Do you really want to be pulling Professional
objects, or is it just a Profile
that you're working with?