drin.dc
drin.dc

Reputation: 105

Which is better: Constants or Models in Rails

Suppose I have a model called Staff. The staff object is attached to a user and has a "title". Now, this title can either be a manager, doctor, engineer, nurse. Which is a better practice?

Declare a constant and have a title attribute on the Staff model itself

ROLES = ["manager", "doctor", "engineer", "nurse"]

or create a model called Role and set it as a relationship to the Staff model? This model will ONLY have one attribute called title.

I'm pretty much aware that both will work, but then I just want to know you guys' perspectives/ideas regarding this one moving forward.

Thanks!

Upvotes: 3

Views: 158

Answers (4)

max
max

Reputation: 101811

This completely depends on the use case. If you can foresee that users in you application will only ever have one role - then use an enum as suggested by Sergii. It is going to be the simplest and most performant option.

It also depends on if you actually need a role system for authorization or if you are simply checking that users can not title themselves whatever they please.

If you need a flexible system where users can have multiple roles than you will want to use a database table.

Another question is how roles are created, is it good enough that it is a developer concern? Could there be a need to be able to create role definitions from a GUI?

This example shows a common setup with a table for role definitions and a join table containing the roles assigned to users.

class User < ActiveRecord::Base
  has_many :user_roles
  has_many :roles, through: :user_roles

  def has_role?(name, resource = nil)
    scope = user_roles.joins(:role).where(
      role: { name: name }
    )
    scope = scope.where(resource: resource) if resource
    scope.any?
  end
end

# join table with roles assigned to users
class UserRole < ActiveRecord::Base
  belongs_to :user
  belongs_to :role
  belongs_to :resource, polymorphic: :true
end 

# Role definitions
class Role < ActiveRecord::Base
  has_many :user_roles
  has_many :users, through: :roles
end

Upvotes: 3

Giuse
Giuse

Reputation: 126

Always start small, get your tests green, then refactor when need arises.

If you only want to define a role for people, that's just as simple as you thought:

  • new attribute title to the Staff model table
  • new constant TITLES in the Staff model
  • new validation in the Staff model, as suggested by @petr-gazarov

Should the need arise for a new model, be sure, you will have no choice but refactoring into that. As long as you have a choice, the answer is always the simplest.

Soon though, you may start asking yourself what you want to do with this role differentiation -- spoiler, you probably want to learn more about authorization (e.g. "what can do", vs. authentication "who is").
So I'll kill two birds with one stone and link you a good guide on role-based authorization, from a very easy and functional gem for defining role-based authorization: cancancan.

Upvotes: 1

Petr Gazarov
Petr Gazarov

Reputation: 3811

Sounds like you just want title attribute on Staff.

If you want to ensure that title is one of 'permitted' values, you can validate inclusion on model level:

validates :title, inclusion: { in: %w(manager doctor engineer nurse) }

Upvotes: 2

Sergii K
Sergii K

Reputation: 845

If your role system is so simple - there is no need to create another model with one field.

To implement that feature I recommend you to look at rails ActiveRecord#enum: http://edgeapi.rubyonrails.org/classes/ActiveRecord/Enum.html

Upvotes: 9

Related Questions