Ruby beginner over here. I'm currently working on a project where Users can create public and private wikis. There are three different roles: Admin, Standard User and Premium User. A Standard user can only see public wikis.
Authorization is working properly via Pundit, I've successfully been able to list the wikis I want each user to have access to, but I'm missing one thing: Wikis to which the Standard user has been added as a collaborator.
Collaborator is not a role, so I'm having difficulty adding those wikis to the index (Wiki has many users through collaborators).
If you have any ideas on how I can implement this, or perhaps an alternative route, I'd be happy to hear them.
Currently, a standard user can access only public wikis and wikis he or she has created (which are public by default). Currently, the Standard user can view and edit a private wiki he or she has been added to but only by visiting the direct wiki url.
This is what my code looks like so far:
wiki.rb
class Wiki < ActiveRecord::Base
belongs_to :user
has_many :collaborators
has_many :users, through: :collaborators
scope :visible_to, -> (user) { user && (user.premium? || user.admin?) ? all : where(public: true) }
scope :publicly_visible, -> {where(public: true)}
end
wiki_policy.rb
class WikiPolicy < ApplicationPolicy
def index?
true
end
def show?
record.public? || user.present? && (record.user == user || user.admin? || user.premium? || record.users.include?(user))
end
def create?
user.present?
end
def new?
create?
end
def update?
user.present? && (record.user == user || user.admin? || record.users.include?(user))
end
def destroy?
update?
end
class Scope
attr_reader :user, :scope
def initialize(user, scope)
@user = user
@scope = scope
end
def resolve
if @user.present?
wikis = Wiki.visible_to(@user)
else
wikis = Wiki.publicly_visible
end
end
end
end
wikis_controller.rb
class WikisController < ApplicationController
def index
@wikis = policy_scope(Wiki).paginate(page: params[:page], per_page: 10)
authorize @wikis
end
...
end
Thanks Michael!
Originally, I had a problem where will_paginate was not working after trying to build the array of wikis via the scope. That's why we went a different route and used the visible_to, etc scopes in the Wiki Model.
However, while trying to fix this, I found a way to keep our array the way the checkpoint intended and also to paginate them.
My policy now looks like this:
class WikiPolicy < ApplicationPolicy
def index?
true
end
def show?
record.public? || user.present? && (record.user == user || user.admin? || user.premium? || record.users.include?(user))
end
def create?
user.present?
end
def new?
create?
end
def update?
user.present? && (record.user == user || user.admin? || record.users.include?(user))
end
def destroy?
update?
end
class Scope
attr_reader :user, :scope
def initialize(user, scope)
@user = user
@scope = scope
end
def resolve
wikis = []
if user.present?
if user.admin?
wikis = scope.all
elsif user.premium?
all_wikis = scope.all
all_wikis.each do |wiki|
if wiki.public? || wiki.user == user || wiki.users.include?(user)
wikis << wiki
end
end
else
all_wikis = scope.all
wikis = []
all_wikis.each do |wiki|
if wiki.public? || wiki.users.include?(user)
wikis << wiki
end
end
end
end
wikis
end
end
end
wiki.rb
class Wiki < ActiveRecord::Base
belongs_to :user
has_many :collaborators
has_many :users, through: :collaborators
end
I was able to paginate and show the correct list of wikis (including collab wikis) by adding the following file in config/initializers
array_paginate.rb
require 'will_paginate/array'