title: Rails: Using CanCan to define multiple roles depending on instances of single Model?
body: <p>I'm currently stuck on how to separate roles for CanCan depending on each condition that we want.
  In our application, there are many categories (such as math, english, history, etc.) and within each are many courses. </p>
  
  <p>Each user can have many different roles on each category. For example, John can be a "reader" for math, which means he can read all the courses that are in math. John can also be a "writer" for english, which means he can read all the courses in english, create a course within category english, and edit/delete only the courses he created within english. </p>
  
  <p>If these were the only roles John had, he would not be able to see the category history in the navbar, and would be denied access to courses that are within history.</p>
  
  <p>These are how relations are set up:</p>
  
  <pre><code>class User &lt; ActiveRecord::Base
    has_many :roles
  
    def has_role?(role_sym)
      roles.any? { |r| r.level.underscore.to_sym == role_sym }
    end
  end
  
  class Category &lt; ActiveRecord::Base
    has_many :roles
    has_many :courses
  end
  
  class Role &lt; ActiveRecord::Base
    belongs_to :user
    belongs_to :category
    attr_accessible :level, :category_id, :user_id
  end
  </code></pre>
  
  <p>in model/ability.rb we have</p>
  
  <pre><code>class Ability
  include CanCan::Ability
  
  def initialize(user)
    user ||= User.new # guest user (not logged in)  #guest
  
    if user.has_role? :reader 
       reader(user)
    end
  
    if user.has_role? :writer
      writer(user)
    end
  end
  
  #BE ABLE TO SEE COURSES AND CATS FOR PERMITTED CATS.
  def reader(user)
    can :read, Category, :roles =&gt; { :user_id =&gt; user.id, :level =&gt; "reader" }
     ## how would we be able to limit reading of courses that are within permitted categories? something like category.courses ~~ 
  end
  
  def writer(user)
    reader(user) #inheriting from reader? this doesnt work because level is hardcoded into reader
    can :read, Category, :roles =&gt; { :user_id =&gt; user.id, :level =&gt; "writer"}
    # 1.you can read all courses in category that you are given permission to
    # 2.you can write courses in permitted category
    # 3.you can edit, delete courses that only youve created within permitted category
  end
  end
  </code></pre>
  
  <p>Questions:</p>
  
  <ol>
  <li><p>How do we separate the roles of "reader" and "writer" in the correct way? How do we access the courses that are within the categories that we have access to?</p></li>
  <li><p>After defining the reader and writer methods in the ability.rb, how do we use them in our view pages? It looks like the current documentations use something like "&lt;% if can? :read, @category %>
  " but that doesn't use the methods we separated and defined. </p></li>
  </ol>
  
  <p>p.s. We will have 7 different roles: guest, reader, writer, editor, manager, admin, and app_admin(our developers) </p>
  
  <p>I've been trying to solve this for 3 days now - please understand that I'm still fairly a beginner! Thanks in advance</p>