mirror of
https://github.com/varvet/pundit.git
synced 2022-11-09 12:30:11 -05:00
Add controller helpers which facilitate update and build patterns
This commit is contained in:
parent
1bfe952d09
commit
8dedc55bf8
5 changed files with 129 additions and 5 deletions
43
README.md
43
README.md
|
|
@ -536,6 +536,49 @@ class PostsController < ApplicationController
|
|||
end
|
||||
```
|
||||
|
||||
## Controller helpers
|
||||
|
||||
Pundit provides controller helpers which take the permitted attributes pattern to its
|
||||
logical conclusion. We can write an update action like this:
|
||||
|
||||
```ruby
|
||||
# app/controllers/posts_controller.rb
|
||||
class PostsController < ApplicationController
|
||||
def update
|
||||
@post = Post.find(params[:id])
|
||||
if update(@post)
|
||||
redirect_to @post
|
||||
else
|
||||
render :edit
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
Not only does this correctly retrieve the permitted attributes and assign them,
|
||||
it also makes sure authorization is performed both before *and* after we set
|
||||
these attributes. For example, an admin might be able to assign everyone a
|
||||
particular task, but a regular user can only assign themselves, not someone
|
||||
else. This kind of scenario requires the permissions to be checked twice, both
|
||||
before (they can't edit tasks assigned to someone else) and after (they can't
|
||||
assign a task to someone other than themselves).
|
||||
|
||||
Similarly there is a helper for building a new record:
|
||||
|
||||
```ruby
|
||||
# app/controllers/posts_controller.rb
|
||||
class PostsController < ApplicationController
|
||||
def update
|
||||
@post = build(Post)
|
||||
if @post.save
|
||||
redirect_to @post
|
||||
else
|
||||
render :edit
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
## RSpec
|
||||
|
||||
### Policy Specs
|
||||
|
|
|
|||
|
|
@ -121,6 +121,20 @@ module Pundit
|
|||
params.require(name).permit(policy(record).permitted_attributes)
|
||||
end
|
||||
|
||||
def build(klass, attributes = {})
|
||||
record = klass.build(attributes)
|
||||
record.assign_attributes(permitted_attributes(record))
|
||||
authorize(record)
|
||||
record
|
||||
end
|
||||
|
||||
def update(record, attributes = {})
|
||||
authorize(record)
|
||||
record.assign_attributes(attributes.merge(permitted_attributes(record)))
|
||||
authorize(record)
|
||||
record.save
|
||||
end
|
||||
|
||||
def policies
|
||||
@_pundit_policies ||= {}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ require "spec_helper"
|
|||
|
||||
describe PostPolicy do
|
||||
let(:user) { double }
|
||||
let(:own_post) { double(user: user) }
|
||||
let(:other_post) { double(user: double) }
|
||||
let(:own_post) { double(user: user, votes: 0) }
|
||||
let(:other_post) { double(user: double, votes: 0) }
|
||||
subject { described_class }
|
||||
|
||||
permissions :update?, :show? do
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
require "spec_helper"
|
||||
|
||||
describe Pundit do
|
||||
let(:user) { double }
|
||||
let(:post) { Post.new(user) }
|
||||
let(:user) { double("user") }
|
||||
let(:post) { Post.new(user: user) }
|
||||
let(:comment) { Comment.new }
|
||||
let(:article) { Article.new }
|
||||
let(:controller) { Controller.new(user, { :action => 'update' }) }
|
||||
|
|
@ -315,4 +315,49 @@ describe Pundit do
|
|||
expect(Controller.new(double, params).permitted_attributes(post)).to eq({ 'votes' => 5 })
|
||||
end
|
||||
end
|
||||
|
||||
describe "#build" do
|
||||
let(:params) { ActionController::Parameters.new({ action: 'update', post: { title: 'Hello', votes: 5, admin: true } }) }
|
||||
let(:controller) { Controller.new(user, params) }
|
||||
|
||||
it "builds a record of the given type with permitted attributes" do
|
||||
post = controller.build(Post, user: user)
|
||||
expect(post).to be_an_instance_of(Post)
|
||||
expect(post.user).to eq(user)
|
||||
expect(post.attributes["title"]).to eq('Hello')
|
||||
expect(post.attributes.has_key?("admin")).to eq(false)
|
||||
end
|
||||
|
||||
it "denies authorization if the given record is not authorized to perform the action" do
|
||||
expect { controller.build(Post, user: double("other")) }.to raise_error(Pundit::NotAuthorizedError)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#update" do
|
||||
let(:params) { ActionController::Parameters.new({ action: 'update', post: { title: 'Hello', votes: 5, admin: true } }) }
|
||||
let(:controller) { Controller.new(user, params) }
|
||||
|
||||
it "builds a record of the given type with permitted attributes" do
|
||||
post = Post.new(title: 'Monkey', user: user)
|
||||
expect(post).to receive(:save).and_return(:done)
|
||||
|
||||
expect(controller.update(post, "body" => "test")).to eq(:done)
|
||||
|
||||
expect(post.user).to eq(user)
|
||||
expect(post.attributes['title']).to eq('Hello')
|
||||
expect(post.attributes['body']).to eq('test')
|
||||
end
|
||||
|
||||
it "denies authorization if the given record is not authorized to perform the action" do
|
||||
post = Post.new(title: 'Monkey', user: double("other"))
|
||||
expect(post).not_to receive(:save)
|
||||
expect { controller.update(post, user: user) }.to raise_error(Pundit::NotAuthorizedError)
|
||||
end
|
||||
|
||||
it "denies authorization if the given record is not authorized after attributes are assigned" do
|
||||
post = Post.new(title: 'Monkey', user: user)
|
||||
expect(post).not_to receive(:save)
|
||||
expect { controller.update(post, user: double("other")) }.to raise_error(Pundit::NotAuthorizedError)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ end
|
|||
|
||||
class PostPolicy < Struct.new(:user, :post)
|
||||
def update?
|
||||
post.user == user
|
||||
post.user == user and post.votes < 10
|
||||
end
|
||||
def destroy?
|
||||
false
|
||||
|
|
@ -43,15 +43,37 @@ class PostPolicy < Struct.new(:user, :post)
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
class PostPolicy::Scope < Struct.new(:user, :scope)
|
||||
def resolve
|
||||
scope.published
|
||||
end
|
||||
end
|
||||
|
||||
class Post < Struct.new(:user)
|
||||
attr_reader :attributes
|
||||
singleton_class.send :alias_method, :build, :new
|
||||
|
||||
def self.published
|
||||
:published
|
||||
end
|
||||
|
||||
def initialize(attributes = {})
|
||||
@attributes = attributes
|
||||
end
|
||||
|
||||
def user
|
||||
@attributes[:user]
|
||||
end
|
||||
|
||||
def votes
|
||||
@attributes[:votes].to_i
|
||||
end
|
||||
|
||||
def assign_attributes(attributes)
|
||||
@attributes.merge!(attributes)
|
||||
end
|
||||
|
||||
def to_s; "Post"; end
|
||||
def inspect; "#<Post>"; end
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue