0

I have a Rails app, using CanCan for authorization. I have Users that can have Memberships in Groups. I would like to create a CanCan rule that authorizes a user to create a group membership only if they are not already a member of that group. So far I have been unable to figure out the magic:

class Group < ActiveRecord::Base
  has_many :memberships
  has_many :members, :through => :memberships
end

class Membership < ActiveRecord::Base
  belongs_to :member, class_name: 'User'
  belongs_to :group
end

class User < ActiveRecord::Base
  has_many :groups, through: :memberships
  has_many :memberships, foreign_key: 'member_id'
end

class Ability
  include CanCan::Ability

  def initialize(user)

    user ||= User.new

    # One of my sad attempts...
    can :create, Membership do |membership|
      !membership.group.has_member(user)
    end

  end

end

<% if can? :create, Membership %>
   View code for creating memberships...
<% end %>

Your assistnace is appreciated. Also, I do understand that CanCanCan is the successor of CanCan. A CanCanCan answer will be fine.

SingleShot
  • 18,821
  • 13
  • 71
  • 101
  • From this [wiki](https://github.com/CanCanCommunity/cancancan/wiki/Defining-Abilities#hash-of-conditions) you may want to try ````can :create, Membership, Membership do |membership|```` – mintuhouse Jan 26 '15 at 01:52
  • If you have an example that will work that would be great. – SingleShot Jan 26 '15 at 02:25

1 Answers1

1

CanCan blocks only work on model instances (see this wiki page) and right now your can? sends the model class, not an instance.

In order for this to work, you need to pass an instance of Membership. You can do something like this (assuming @group is the group the user wants to join)

<% if can? :create, Membership.new(group: @group) %>
   View code for creating memberships...
<% end %>

I would recommend, however, that you put the ability on the group. In my opinion, it is a more direct approach.

ability.rb

class Ability
  include CanCan::Ability

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

    can :join, Group do |group|
      !group.members.include?(user)
    end
  end
end

View

<% if can? :join, @group %>
   View code for creating memberships...
<% end %>
Daniel Ma
  • 646
  • 5
  • 10
  • Your first answer does not work for me - perhaps a conflict with other rules I have defined. Your second, preferred, answer does the trick! – SingleShot Jan 31 '15 at 19:30
  • I'm thinking the second answer can be gotten around by a savvy user and an HTTP client though. I think I need to get your first answer working to protect against that. UPDATE: got it working. – SingleShot Jan 31 '15 at 19:31