Railsの権限管理にPunditを使うようになったのですが、標準のmatcherだとオブジェクトの操作に対して挙動を記述していきます。そうすると、あるユーザがどのような権限をもっているかがわかりにくい気がしました。
まず、Punditの標準でついてくるmatcherだとREADMEにあるようにこのような記述をします。
spec/policies/post_spec.rb
describe PostPolicy do
subject { described_class }
permissions :update? do
it "denies access if post is published" do
expect(subject).not_to permit(User.new(:admin => false), Post.new(:published => true))
end
it "grants access if post is published and user is an admin" do
expect(subject).to permit(User.new(:admin => true), Post.new(:published => true))
end
it "grants access if post is unpublished" do
expect(subject).to permit(User.new(:admin => false), Post.new(:published => false))
end
end
end
アプリケーションによっては、ユーザの権限がどのような操作ができるのかという書き方をしたほうがわかりやすい場合があるのではないでしょうか。そのような場合は、ここで紹介しているやり方が参考になりました。
spec/support/pundit_mathcer.rb
を作成します。
spec/support/pundit_mathcer.rb
RSpec::Matchers.define :authorize do |action|
match do |policy|
policy.public_send("#{action}?")
end
failure_message_for_should do |policy|
"#{policy.class} does not permit #{action} on #{policy.record} for #{policy.user.inspect}."
end
failure_message_for_should_not do |policy|
"#{policy.class} does not forbid #{action} on #{policy.record} for #{policy.user.inspect}."
end
end
spec/rails_helper.rb
に下記を追加します。
spec/rails_helper.rb
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
そうすると、このような記述でPolicyのspecが書けます。
spec/policies/article_spec.rb
require 'rails_helper'
describe ArticlePolicy do
subject { ArticlePolicy.new(user, article) }
let(:article) { FactoryGirl.create(:article) }
context "for a visitor" do
let(:user) { nil }
it { should authorize(:show) }
it { should_not authorize(:create) }
it { should_not authorize(:new) }
it { should_not authorize(:update) }
it { should_not authorize(:edit) }
it { should_not authorize(:destroy) }
end
context "for a user" do
let(:user) { FactoryGirl.create(:user) }
it { should authorize(:show) }
it { should authorize(:create) }
it { should authorize(:new) }
it { should authorize(:update) }
it { should authorize(:edit) }
it { should authorize(:destroy) }
end
end