Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails model structure for users

I'm new to rails, and I'm working on my second rails app.

The app will have different roles for users, but some users will have multiple roles.

Every user of the site will be an Artist. Some users will have the role of a moderator.

How would I structure this? In some PHP apps I've used, there is only one user, and then a database column for is_admin, etc. But I've looked at the source for rails apps and have seen separate models for User and Admin, etc. although I'm not sure why.

So, should I have a single User model with a role attribute, which could be Moderator, and then just call Users "Artists" in my views, routes, etc.?

Or should I have a User model, a Moderator model which inherits from it, and an Artist model which belongs_to User?

I'm really confused.

like image 409
Scott Avatar asked Sep 03 '11 11:09

Scott


3 Answers

You can look for gems Devise and CanCan. This pair is really powerful combination. This makes two models User and Role. In Role you can create new roles, without creating new models for them. Although it creates model Ability, here you can define access rules for roles.

Manual: http://www.tonyamoyal.com/2010/07/28/rails-authentication-with-devise-and-cancan-customizing-devise-controllers/

Here you can find Devise's and CanCan's sources and wikies:

https://github.com/plataformatec/devise

https://github.com/ryanb/cancan

My models looks like this:

Role.rb

class Role < ActiveRecord::Base
  has_and_belongs_to_many :users
end

User.rb

class User < ActiveRecord::Base
  has_many :accounts
  has_and_belongs_to_many :roles

  # Include default devise modules. Others available are:
  # :token_authenticatable, :confirmable, :lockable and :timeoutable
  devise :database_authenticatable,
         :recoverable, :rememberable, :trackable, :validatable

  # Setup accessible (or protected) attributes for your model
  attr_accessible :email, :username, :password, :password_confirmation, :remember_me, :role_ids

  def role?(role)
    return !!self.roles.find_by_name(role.to_s.camelize)
  end

end

Ability.rb

class Ability
  include CanCan::Ability

  def initialize(user)
    user ||= User.new # guest user

    if user.role? :administrator
      can :manage, :all
    elsif user.role? :operator
      can :read, Account
      can :read, Server
    elsif user.role? :customer
      can :manage, Account
      can :read, Server
    end
  end
end

In the controller you must add only this two lines:

class YourController < ApplicationController
  before_filter :authenticate_user!
  load_and_authorize_resource

  ...

end
like image 139
Sergey Bezugliy Avatar answered Oct 13 '22 23:10

Sergey Bezugliy


If you need to have code that's specific to and role or such as an admin or moderator one other solution would be to create a base User model that all the other classes inherit from. You can then create an Admin class and a Moderator class that inherit from the User model. This would mean you can avoid constantly checking the users role in your code e.g. current_user.do_some_admin_thing if current_user.is_admin?. Your classes would look something like this

class User < ActiveRecord::Base
  # base user methods in here
end

class Moderator < User
  def do_moderator_thing
    # perform a moderator task
  end
end

class Admin < Moderator
  def do_admin_thing
    # perform an admin task
  end
end

In this instance the User class has the most basic privileges, moderators can do everything users can plus the moderator specific methods and admins can do everything users and moderators can plus the admin specific methods.

All the different user roles would use the same table in the database but your concerns are neatly separated into classes which avoids excessive conditionals through your code checking what role the user is all the time.

Creating new users would be straightforward also Admin.new :name => 'bob' the Admin class then takes care of how a user is defined as an admin which provides a nice interface where you don't need to know the inner workings of the role system to interact with users.

like image 36
JamieD Avatar answered Oct 13 '22 23:10

JamieD


I think you don't have to create different models because you don't have specific fields for each one. So you just have to set the "role" of each User. Two options : create a role table or add a role field in the table User. Both solutions work, the second is more flexible but less optimized.

But, in your particular case, you don't have a complex role management so you can find a simpler solution. If all of your users are artists you don't have to specify this in your code, it's contained in the implicit description of what a user is. So you just have to save if a user is an admin or not and I think the best solution is to create a boolean field "is_admin".

After that you will have to create some before_filter in your protected controllers, like that :

before_filter => :authorize, :only => :new, :edit, :create, :update, :destroy

def authorize
  redirect_to :root if not current_user.is_admin?
end

And you can have simple requests like that :

@artists = User.all
@moderators = User.where(:is_admin => true)

If you look for a more complete authorization system you can check this small gem : https://github.com/ryanb/cancan

But I think it's not the case for the moment. If you have a simple problem look for a simple solution !

like image 41
Alexandre Butynski Avatar answered Oct 14 '22 01:10

Alexandre Butynski