Enables `policy(:dashboard) # => DashboardPolicy`.
Policies without a matching model can come in handy when a controller
isn't modeled alongside a resource, e.g. a `DashboardsController`.
The policy lookup by symbol also helps with strong parameters,
since I prefer `policy(:post)` or `policy(@post || :post)` over `policy(@post || Post)`.
* The AuthorizationNotPerformedError is very descriptive of the
situation when authorization is forgotten. In the case of no
scoping, it can be a head scratcher.
* This new error type is implemented as a subclass of the current error
type to prevent regressions in existing code. While this is not ideal,
it is the simplest solution I could come up with for compatibility.
Exceptions raised by #authorize now provide the query (e.g. 'create?') and
record (e.g. an instance of 'Post') that caused the exception to be raised, as
well as the relevant policy (e.g. an instance of 'PostPolicy').
NotAuthorizedError is modified to continue to inherit from StandardError, but
now also has attr_accessor values for :query, :record, and :policy.
In controller specs instead of relying on Pundit to instantiate the correct
policy object allow it to be injected into the controller. More often than not
a double is used in controller specs which means the policy cannot be
inferred. This also allows us to double the policy to ensure that on a unit
level the rights methods are being called on callaborators.
class PostsController < ApplicationController
attr_writer :post
helper_method :post
def create
authorize post
post.save
respond_with post
end
private
def post
@post ||= Post.new post_attributes
end
end
describe PagesController do
let(:policy) { double 'SomePolicy', create?: true }
before do
controller.policy = policy
end
it 'delegates authorization to policy' do
expect(policy).to have_received(:create?)
end
end
Add spec for injecting policy.
Use `or` instead of ternary operator.
Allow policy_scope to be injected for controller tests.
See the readme changes for an example. In short, this behaves
like verify_authorized but is useful for actions that find a
collection (like index) and don't authorize instances.
The `BlogPolicy -> "BlogPolicy" -> "Blog" -> "BlogPolicy" -> BlogPolicy`
issue @jnicklas pointed out has been resolved. For example, given
```ruby
class BlogPolicy < Struct.new(:user, :blog); end
class Blog; end
class ArtificialBlog < Blog
def self.policy_class
BlogPolicy
end
end
```
The above string manipulation/casting is prevented; the `BlogPolicy`
class will be immediately returned to `policy` and on to be evaluated.
Anonymous classes are now supported too. For example, given
```ruby
class BlogPolicy < Struct.new(:user, :blog); end
class Blog; end
class ArtificialBlog < Blog
def self.policy_class
Struct.new(:user, :blog) do
def create?
true
end
end
end
end
```
The `Struct` will be returned and evaluated as any other policy.