Search code examples
ruby-on-railscancanuser-rolescancancan

Rails Devise Role Model & CanCanCan - defining abilities


I'm trying to define abilities in CanCanCan.

I can't figure out the syntax to get started.

I use Role Model for my roles and the roles are defined in my Profile.rb. Profile.rb belongs to User.rb.

I am trying to check if the user has the role :student.

When I try:

if {user_signed_in?, user.profile.has_role? :student}

I get a syntax error that says:

syntax error, unexpected ',', expecting =>
    if {user_signed_in?, user.profile.has_role? :student}

When I try:

if {user_signed_in? && user.profile.has_role? :student}

I get a syntax error that says:

syntax error, unexpected tSYMBEG, expecting =>
    if {user_signed_in? && user.profile.has_role? :student}

I have also tried replacing the curly braces with regular brackets and removing them altogether.

When I try removing the devise part (user_signed_in) and using the suggestion in the comments below, I try:

if  user.profile.has_role?(:student) 

And I get this error:

undefined method `has_role?' for nil:NilClass

When I try:

if  user.profile.has_role? :student 

I get this error:

undefined method `has_role?' for nil:NilClass

When I try:

if  user.profile.has_role?(student) 

I get this error:

undefined local variable or method `student' for #<Ability:0x007fd034a78968>

I have the following roles defined in my profile.rb:

  roles :admin, :manager, #
        :student, :educator, :researcher, :ktp, :faculty_manager, :ip_asset_manager,  # for universities
        :sponsor, # for industry
        :project_manager, :representative, # for both uni and industry
        :grantor, :investor, :adviser, :innovation_consultant, :panel_member, # external
        :participant, :guest # public

When I try:

can :crud, Profile, :user_id => user.id if  user.profile.has_role? :student

I don't get any errors, but my problem with this approach is that a student can do many things (there are 10 lines of permissions, so I would need to add the if statement individually to each of the 10 'can' statements, unless there is a way that the if statement can be applied to all lines, before the next 'elsif' statement.

The first part of my ability.rb is pasted below (there are a lot of roles and a lot of abilities, so I haven't pasted the whole thing).

class Ability
  include CanCan::Ability

  def initialize(user)

      alias_action :create, :read, :update, :destroy, :to => :crud


    # Define abilities for the passed in user here. For example:
    #
    user ||= User.new # guest user (not logged in)

      #users who are not signed in can create registration or login 

      # can read publicly available projects, programs and proposals
      can :read, Project, {:active => true, :closed => false, :sweep => { :disclosure => { :allusers => true } } }

      # {:active => true, :closed => false  &&  :Project.sweep.disclosure.allusers => true}
      # if user role is student

      can :crud, Profile, :user_id => user.id if  user.profile.has_role? :student #[for themselves]
      can :read, ProjectInvitation, {:student_id => @current_user && :expiry_date_for_students_to_claim_project > Time.now}
      #  can read project invitations addressed to them
      # can read projects to which they are invited whilst the invite is available to accept;
      can :read, Project, {} #if invited to the project?
      # and they can create responses to invitations to those projects
      can :update, ProjectInvitation.student_accepted
      # they can create questions on those projects before the invite expiry date;
      can :read, ProjectQuestion, {} #if intvited
      can [:create, :update, :destroy], ProjectQuestion #if they created the question
      can :read, ProjectAnswer #if its on a project they can see
      # they can update term sheets and template agreements for those projects
      can [:read, :update], TermSheet #{where created for project in which they are participating}
      can [:read, :update], ProjectAgreement #{where created for a project in which they are participating}
      can [:read, :update], FinanceAgreement #{where created for a project in which they are participating}
      can [:read, :update], Nda #{where created for a project in which they are participating}
      can [:create, :update], Feedback #{where created for a project in which they are participating and the feedback is on other project team members and the project is completed}


    elsif user.profile.has_role? :educator

When I try (the suggestion below):

if  user.try(:profile).present? && user.profile.has_role? :student 

I get this error:

syntax error, unexpected tSYMBEG, expecting keyword_then or ';' or '\n'
...nt? && user.profile.has_role? :student

Please can someone see what I'm doing wrong?


Solution

  • For following codes

    if {user_signed_in?, user.profile.has_role? :student}
    if {user_signed_in? && user.profile.has_role? :student}
    if {user_signed_in? && user.profile.has_role? :student}
    

    You can not user {} for an ruby statement, it expect a key value pair. You can rewrite your code as following

    if user_signed_in? && user.profile.has_role? :student
    

    But you are getting an null pointer errors, so you have fix it in you codes as following, first check if user.try(:profile).present? then you can call user.profile.has_role? :student because your profile getting nil.

    if user.try(:profile).present? && user.profile.has_role?(:student)