cancan abilities in separate file

2020-03-07 05:26发布

问题:

Is it possible to define abilities in separate file and include them in ability.rb file inside initialize method ?

belowed code returns: tried and got: undefined method 'can'

ability.rb

def initialize(user)
  include MyExtension::Something::CustomAbilities
  ...
end

lib/my_extension/something.rb

module MyExtension::Something

  module CustomAbilities
      can :do_it, Project do |project|
      check_something_here and return true or false...
    end
  end

end

perfect solution, if possible, would be to extend class Ability with Ability.send :include/extend, so without explicit include in initialize method

回答1:

Just Include the Module and Call the Method in initialize

The trick here is to create modules for each of your abilities, include them in your base ability.rb file and then run the specific method in your initialize method, like so:

In your ability.rb file:

class Ability

  include CanCan::Ability

  include ProjectAbilities 

  def initialize user
    # Your "base" abilities are defined here.

    project_abilities user
  end  

end

In your lib/project_abilities.rb file:

module ProjectAbilities

  def project_abilities user
    # New abilities go here and later get added to the initialize method
    # of the base Ability class.

    can :read, Project do |project|
      user.can? :read, project.client || user.is_an_admin?
    end 
  end

end

Using this pattern, you can break out your abilities into various modules (perhaps, one for each model that you have to define user abilities for).

Take a Look at Pundit

Also of note, take a look at the (relatively) new gem called Pundit, which provides a much more scalable pattern for authorization of larger sites.

Cheers,

JP



回答2:

With more modern Rubies, you can achieve this with prepend

module CashBalance
  attr_accessor :balance

  def deposit(amount)
    self.balance += amount
  end

  def withdraw(amount)
    self.balance -= amount
  end

  def initialize(*args)
    self.balance = 0.0
    super
  end  

end

class Bank
  prepend CashBalance

  def initialize(name)
    @name = name
  end

  def dump
    puts "%s has a balance of %0.2f" % [ @name, balance ]
  end


end


b = Bank.new("Fleet")

b.deposit(20)
b.dump
b.withdraw(10)
b.dump

yields

$ ruby blarg.rb
Fleet has a balance of 20.00
Fleet has a balance of 10.00