Created
November 24, 2017 13:20
-
-
Save npetkov/e7e6ff57cddacae62c19babdaeaeb7e6 to your computer and use it in GitHub Desktop.
CanCan::Ability#merge transferring only rules
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
begin | |
require 'bundler/inline' | |
rescue LoadError => e | |
$stderr.puts 'Bundler version 1.10 or later is required. Please update your Bundler' | |
raise e | |
end | |
gemfile(true) do | |
source 'https://rubygems.org' | |
gem 'rails', '5.1.0' # use correct rails version | |
gem 'cancancan' # use correct cancancan version | |
gem 'sqlite3' # use another DB if necessary | |
end | |
require 'active_record' | |
require 'cancancan' | |
require 'cancan/model_adapters/active_record_adapter' | |
require 'cancan/model_adapters/active_record_4_adapter' | |
require 'minitest/autorun' | |
require 'logger' | |
# This connection will do for database-independent bug reports. | |
ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:') | |
ActiveRecord::Base.logger = Logger.new(STDOUT) | |
# create your tables here | |
ActiveRecord::Schema.define do | |
create_table :books, force: true do |t| | |
t.integer :user_id | |
end | |
create_table :users, force: true do |t| | |
t.string :name | |
t.string :role | |
end | |
end | |
class Book < ActiveRecord::Base | |
belongs_to :user | |
end | |
class User < ActiveRecord::Base | |
ROLES = { | |
user: 'user', | |
admin: 'admin' | |
}.freeze | |
has_many :books | |
def role_in?(roles) | |
roles.map { |role| self.class::ROLES[role] }.include? role | |
end | |
end | |
class AbilityBuilder | |
attr_reader :ability, :user | |
def initialize(ability) | |
@ability = ability | |
@user = ability.user | |
end | |
def apply(other) | |
target = build_target.extend(other) | |
target.abilities(user) | |
ability.merge(target) | |
self | |
end | |
def build | |
ability | |
end | |
private | |
def build_target | |
target = Object.new | |
target.extend(CanCan::Ability) | |
end | |
end | |
module RoleAbility | |
def authorized?(user) | |
user.role_in?(roles) | |
end | |
def abilities(_user) | |
raise NotImplementedError | |
end | |
def roles | |
raise NotImplementedError | |
end | |
end | |
module AdminRoleAbility | |
include RoleAbility | |
def abilities(user) | |
manage_books if authorized?(user) | |
end | |
def roles | |
%i[admin] | |
end | |
private | |
def manage_books | |
alias_action :create, :read, :update, :destroy, to: :crud | |
can :crud, Book | |
end | |
end | |
class Ability | |
KNOWN_ABILITIES = [AdminRoleAbility].freeze | |
include CanCan::Ability | |
attr_reader :user | |
def initialize(user) | |
@user = user | |
builder = AbilityBuilder.new(self) | |
KNOWN_ABILITIES.reduce(builder) { |memo, ability| memo.apply(ability) } | |
end | |
end | |
class BugTest < Minitest::Test | |
def test_bug | |
user = User.create!(role: 'admin') | |
Book.create([{user: user}]) | |
ability = Ability.new(user) | |
books = Book.accessible_by(ability, :destroy).count | |
assert_equal 1, books | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment