0

I am trying to figure out the best way to accomplish my problem. I've got a pages table, and a user_types table. I am trying to specify multiple user types on a page. They will act as permission groups. I need to do this twice however. Once for read permissions, and once for edit permissions. Here is an example:

Home page has 3 user types that can read it - admin, super admin, public It has 2 user types that can edit it - admin, super admin

I have one user_types table: admin super admin public etc

I have created two mapping tables (one for read, and one for edit): pages_user_read_types pages_user_edit_types

they both have page_id, and user_type_id

Is there a better way to accomplish this? If this is the best way, I need help figuring out the relationships for the models. I have this for one relationship

  has_and_belongs_to_many :user_types, :join_table => :pages_user_read_types

How do i specify two relationships for seperate fields?

Thanks

Sean
  • 1,078
  • 14
  • 25

2 Answers2

1

At the very least, you probably want to add a Permission model. If it ever gets more complicated than what you've described, I would also recommend using CanCan.

class Permission < ActiveRecord::Base
  #table is id, page_id, user_type_id, and permission_type (string).
  belongs_to :page
  belongs_to :user_type
end

In your controller, you can construct a filter chain like this:

class PagesController < ApplicationController
  before_filter :load_page
  before_filter :authorize_view!, only: [ :show ]
  before_filter :authorize_edit!, only: [ :edit ]

  def show
  end

  def edit
  end

  private
    def load_page
      @page = Page.find(params[:id])
    end

    def authorize_view!
      if !@page.permissions.where(user_type_id: current_user.user_type_id, permission_type: "view").exists?
        flash[:notice] = "You do not have permission to view that page."
        redirect to root_path
      end
    end

    def authorize_edit!
      if !@page.permissions.where(user_type_id: current_user.user_type_id, permission_type: "edit").exists?
        flash[:notice] = "You do not have permission to edit that page."
        redirect to root_path
      end
    end

end

(This assumes you have a current_user method in your app).

Zach Kemp
  • 11,736
  • 1
  • 32
  • 46
  • I actually am using Cancan. I should have explained that part. Basically a user is assigned to a user type, and the user types are what I am trying to map. Thanks for your help! – Sean Oct 22 '12 at 06:24
1

The HABTM relationship in Rails has seemed to fall out of favor over the last couple of years with Rails developers to the has_many :through relationship. The only time you should use HABTM is when you have no need for any additional information about the relationship between two models. In your case, you are trying to emulate this by creating two HABTM relationships when you could effectively accomplish by having a join model with a editable attribute.

In code, it would look something like this:

class Page < ActiveRecord::Base
  has_many :page_permissions
  has_many :user_types, :through => page_permissions

  def editable_user_types
    page_permissions.includes(:user_types).where(:editable => true).map(&:user_type)
  end

  def read_only_user_types
    page_permissions.includes(:user_types).where(:editable => false).map(&:user_type)
  end      
end

class PagePermission < ActiveRecord::Base
  belongs_to :page
  belongs_to :user_type
  # When you create this model, you should have a boolean attribute for editable
end

class UserType < ActiveRecord::Base
  has_many :page_permissions
  has_many :pages, :through => :page_permissions
end

I think following this approach will allow you to consolidate to one join table which will be better in the future if you need to add additional attributes to the relationship (PagePermission) between Page and UserType.

adimitri
  • 1,296
  • 9
  • 13
  • I like that idea much better. I've changed everything to follow what you have here in your example. I'm having trouble now with the relationship part. Typically in a habtm i have been able to just do something like "page.read_only_users". Right now i am having trouble getting that part to work. Any ideas on what i'm doing wrong? – Sean Oct 22 '12 at 06:05
  • Read only *Users*? Or Read only *UserTypes*? If the former, what is the relationship between *UserType* and *User*? *UserType* `has_many :users` and *User* `belongs_to :user_type`? – adimitri Oct 22 '12 at 06:38
  • UserTypes. I have users, but there are a lot. So to simplify, each user has a user type. Then when a page is created, read rights can be assigned to one or many user types, and same goes with editing rights for a page. A user has_one user type. And You could say that user types has many users. I hope that makes sense, and thanks again for the help! – Sean Oct 22 '12 at 06:45
  • Ok, that makes sense. I guess what I don't understand is in your original comment, you said you wanted to do something like `page.read_only_users`. In my answer, I have a method `page.read_only_user_types`. So in that method, I'm returning the *UserType*s for a page that are non-editable. Are you saying that method is giving you an error/it's not working, or it's not returning what you want? – adimitri Oct 22 '12 at 06:50
  • Oh i'm sorry. I'm trying to use it on an edit field. I am trying to display a multi select box (in activeadmin). And it just displays an empty array. So I need to figure out how to map the relationship so when I add a new page it displays all available user types. I guess I could do UserTypes.all, but is there a different way to set up the relationship so it acts the same as a habtm? Forgive my questions, i'm fairly new to all of this. – Sean Oct 22 '12 at 06:57
  • I'm afraid I don't have any experience with *ActiveAdmin* so I'm not sure how to configure it so that it can work in this instance. I tried to skim the documentation but it's not something I'd be able to figure out without using it myself. I guess I don't understand why `UserType.all` wouldn't be the desired behavior here? Because if you wanted to be able to set another read-only *UserType*, you would want to be able to select from the entire list, not the list of already set *UserType*'s. – adimitri Oct 22 '12 at 07:31
  • Ok, so even if I use the UserType.all, it doesn't create a resource that will save to the mapping table. That, or i'm doing it wrong... How do I do it so that when I submit a form with "read_only_user_types", it knows what to do with it. Right now it is just giving me an unkown attribute error. Any ideas? Thanks – Sean Oct 23 '12 at 02:27
  • This is where I don't know the particulars of *ActiveAdmin*. But what you're going to need to do is create some setter method for the read_only_user_types like `read_only_user_types=`. I won't be able to help you with that other than to let you know that's where it seems your problem is coming from. It might also be the case that *ActiveAdmin* is unable to support complicated logic between models and is only good for basic ones, at least at the moment. – adimitri Oct 23 '12 at 08:44
  • Thanks for your help. I'll look into setter methods! – Sean Oct 23 '12 at 15:34