According to the Pundit readme authorize
should return the record, yet when I'm calling it I'm getting true
.
authorize returns the object passed to it, so you can chain it like this:
Controller:
def show @user = authorize User.find(params[:id]) end
Gemfile:
gem 'rails', '~> 5.1.1'
gem 'devise', '~> 4.3'
gem 'pundit', '~> 1.1'
My controller:
class PostsController < ApplicationController
skip_before_action :authenticate_user!, only: [:show, :index]
before_action :set_post, only: [:show, :edit, :update, :destroy]
def show
# just for debugging purposes
raise "Post is a #{@post.class.name}!" unless @post.is_a? Post
end
def set_post
# this should return an instance of post
@post = authorize Post.find(params[:id])
end
end
Policy:
class PostPolicy < ApplicationPolicy
class Scope < Scope
def resolve
scope.all
end
end
def show?
true
end
# ...
end
Spec:
require 'rails_helper'
RSpec.describe "Posts", type: :request do
subject { response }
describe "GET /posts/:id" do
let!(:post) { create(:post) }
before { get post_path(post) }
it { should be_successful }
end
end
Failure message:
4) Posts GET /posts/:id
Failure/Error: raise "Post is a #{@post.class.name}!" unless @post.is_a? Post
RuntimeError:
Post is a TrueClass!
While its pretty simple to remedy this by:
def set_post
@post = Post.find(params[:id]).tap do |p|
@post = Post.find(params[:id]).tap { |r| authorize r }
end
end
Im very curious as to why its not working as stated by the readme. Is this a bug or am I just missing something?
Returning the record is apparently a change in the master that is not reflected in the 1.1 release.
# Retrieves the policy for the given record, initializing it with the
# record and user and finally throwing an error if the user is not
# authorized to perform the given action.
#
# @param user [Object] the user that initiated the action
# @param record [Object] the object we're checking permissions of
# @param record [Symbol] the query method to check on the policy (e.g. `:show?`)
# @raise [NotAuthorizedError] if the given query method returned false
# @return [true] Always returns true
def authorize(user, record, query)
policy = policy!(user, record)
unless policy.public_send(query)
raise NotAuthorizedError, query: query, record: record, policy: policy
end
true
end
A workaround is:
def authorize(record, query = nil)
super
record
end