From 4f22dc15685145f909a96abfe0fa8616f508d1ca Mon Sep 17 00:00:00 2001 From: David Singer Date: Fri, 20 Nov 2015 19:02:56 -0800 Subject: [PATCH] Better handling of policy finder for arrays and scoped models --- lib/pundit/policy_finder.rb | 28 ++++++---- spec/pundit_spec.rb | 107 +++++++++++++++++++++++++++++++----- spec/spec_helper.rb | 15 +++++ 3 files changed, 124 insertions(+), 26 deletions(-) diff --git a/lib/pundit/policy_finder.rb b/lib/pundit/policy_finder.rb index 51889e9..8ac22b3 100644 --- a/lib/pundit/policy_finder.rb +++ b/lib/pundit/policy_finder.rb @@ -40,21 +40,27 @@ module Pundit elsif object.class.respond_to?(:policy_class) object.class.policy_class else - klass = if object.respond_to?(:model_name) - object.model_name - elsif object.class.respond_to?(:model_name) - object.class.model_name - elsif object.is_a?(Class) - object - elsif object.is_a?(Symbol) - object.to_s.camelize - elsif object.is_a?(Array) - object.join('/').camelize + klass = if object.is_a?(Array) + object.map { |x| find_class_name(x) }.join('::') else - object.class + find_class_name(object) end "#{klass}#{SUFFIX}" end end + + def find_class_name(subject) + if subject.respond_to?(:model_name) + subject.model_name + elsif subject.class.respond_to?(:model_name) + subject.class.model_name + elsif subject.is_a?(Class) + subject + elsif subject.is_a?(Symbol) + subject.to_s.camelize + else + subject.class + end + end end end diff --git a/spec/pundit_spec.rb b/spec/pundit_spec.rb index 7de6fbf..9fa3e9e 100644 --- a/spec/pundit_spec.rb +++ b/spec/pundit_spec.rb @@ -3,13 +3,17 @@ require "spec_helper" describe Pundit do let(:user) { double } let(:post) { Post.new(user) } + let(:post_four_five_six) { PostFourFiveSix.new(user) } let(:comment) { Comment.new } + let(:comment_four_five_six) { CommentFourFiveSix.new } let(:article) { Article.new } let(:controller) { Controller.new(user, { :action => 'update' }) } let(:artificial_blog) { ArtificialBlog.new } let(:article_tag) { ArticleTag.new } let(:comments_relation) { CommentsRelation.new } let(:empty_comments_relation) { CommentsRelation.new(true) } + let(:tag_four_five_six) { ProjectOneTwoThree::TagFourFiveSix.new(user) } + let(:avatar_four_five_six) { ProjectOneTwoThree::AvatarFourFiveSix.new } describe ".authorize" do it "infers the policy and authorizes based on it" do @@ -103,6 +107,93 @@ describe Pundit do expect(policy.comment).to eq Comment end + it "returns an instantiated policy given a symbol" do + policy = Pundit.policy(user, :criteria) + expect(policy.class).to eq CriteriaPolicy + expect(policy.user).to eq user + expect(policy.criteria).to eq :criteria + end + + it "returns an instantiated policy given an array of symbols" do + policy = Pundit.policy(user, [:project, :criteria]) + expect(policy.class).to eq Project::CriteriaPolicy + expect(policy.user).to eq user + expect(policy.criteria).to eq [:project, :criteria] + end + + it "returns an instantiated policy given an array of a symbol and plain model instance" do + policy = Pundit.policy(user, [:project, post]) + expect(policy.class).to eq Project::PostPolicy + expect(policy.user).to eq user + expect(policy.post).to eq [:project, post] + end + + it "returns an instantiated policy given an array of a symbol and an active model instance" do + policy = Pundit.policy(user, [:project, comment]) + expect(policy.class).to eq Project::CommentPolicy + expect(policy.user).to eq user + expect(policy.post).to eq [:project, comment] + end + + it "returns an instantiated policy given an array of a symbol and a plain model class" do + policy = Pundit.policy(user, [:project, Post]) + expect(policy.class).to eq Project::PostPolicy + expect(policy.user).to eq user + expect(policy.post).to eq [:project, Post] + end + + it "returns an instantiated policy given an array of a symbol and an active model class" do + policy = Pundit.policy(user, [:project, Comment]) + expect(policy.class).to eq Project::CommentPolicy + expect(policy.user).to eq user + expect(policy.post).to eq [:project, Comment] + end + + it "returns correct policy class for an array of a multi-word symbols" do + policy = Pundit.policy(user, [:project_one_two_three, :criteria_four_five_six]) + expect(policy.class).to eq ProjectOneTwoThree::CriteriaFourFiveSixPolicy + end + + it "returns correct policy class for an array of a multi-word symbol and a multi-word plain model instance" do + policy = Pundit.policy(user, [:project_one_two_three, post_four_five_six]) + expect(policy.class).to eq ProjectOneTwoThree::PostFourFiveSixPolicy + end + + it "returns correct policy class for an array of a multi-word symbol and a multi-word active model instance" do + policy = Pundit.policy(user, [:project_one_two_three, comment_four_five_six]) + expect(policy.class).to eq ProjectOneTwoThree::CommentFourFiveSixPolicy + end + + it "returns correct policy class for an array of a multi-word symbol and a multi-word plain model class" do + policy = Pundit.policy(user, [:project_one_two_three, PostFourFiveSix]) + expect(policy.class).to eq ProjectOneTwoThree::PostFourFiveSixPolicy + end + + it "returns correct policy class for an array of a multi-word symbol and a multi-word active model class" do + policy = Pundit.policy(user, [:project_one_two_three, CommentFourFiveSix]) + expect(policy.class).to eq ProjectOneTwoThree::CommentFourFiveSixPolicy + end + + it "returns correct policy class for a multi-word scoped plain model class" do + policy = Pundit.policy(user, ProjectOneTwoThree::TagFourFiveSix) + expect(policy.class).to eq ProjectOneTwoThree::TagFourFiveSixPolicy + end + + it "returns correct policy class for a multi-word scoped plain model instance" do + policy = Pundit.policy(user, tag_four_five_six) + expect(policy.class).to eq ProjectOneTwoThree::TagFourFiveSixPolicy + end + + it "returns correct policy class for a multi-word scoped active model class" do + policy = Pundit.policy(user, ProjectOneTwoThree::AvatarFourFiveSix) + expect(policy.class).to eq ProjectOneTwoThree::AvatarFourFiveSixPolicy + end + + it "returns correct policy class for a multi-word scoped active model instance" do + policy = Pundit.policy(user, avatar_four_five_six) + expect(policy.class).to eq ProjectOneTwoThree::AvatarFourFiveSixPolicy + end + it "returns nil if the given policy can't be found" do expect(Pundit.policy(user, article)).to be_nil expect(Pundit.policy(user, Article)).to be_nil @@ -136,20 +227,6 @@ describe Pundit do expect(policy.user).to eq user expect(policy.tag).to eq ArticleTag end - - it "returns an instantiated policy given a symbol" do - policy = Pundit.policy(user, :criteria) - expect(policy.class).to eq CriteriaPolicy - expect(policy.user).to eq user - expect(policy.criteria).to eq :criteria - end - - it "returns an instantiated policy given an array" do - policy = Pundit.policy(user, [:project, :criteria]) - expect(policy.class).to eq Project::CriteriaPolicy - expect(policy.user).to eq user - expect(policy.criteria).to eq [:project, :criteria] - end end end @@ -185,7 +262,7 @@ describe Pundit do expect(policy.criteria).to eq :criteria end - it "returns an instantiated policy given an array" do + it "returns an instantiated policy given an array of symbols" do policy = Pundit.policy!(user, [:project, :criteria]) expect(policy.class).to eq Project::CriteriaPolicy expect(policy.user).to eq user diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index cdbf0dd..aeac8d1 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -96,7 +96,9 @@ end class CriteriaPolicy < Struct.new(:user, :criteria); end module Project + class CommentPolicy < Struct.new(:user, :post); end class CriteriaPolicy < Struct.new(:user, :criteria); end + class PostPolicy < Struct.new(:user, :post); end end class DenierPolicy < Struct.new(:user, :record) @@ -127,3 +129,16 @@ class NilClassPolicy raise "I'm only here to be annoying!" end end + +class PostFourFiveSix < Struct.new(:user); end +class CommentFourFiveSix; extend ActiveModel::Naming; end + +module ProjectOneTwoThree + class CommentFourFiveSixPolicy < Struct.new(:user, :post); end + class CriteriaFourFiveSixPolicy < Struct.new(:user, :criteria); end + class PostFourFiveSixPolicy < Struct.new(:user, :post); end + class TagFourFiveSix < Struct.new(:user); end + class TagFourFiveSixPolicy < Struct.new(:user, :tag); end + class AvatarFourFiveSix; extend ActiveModel::Naming; end + class AvatarFourFiveSixPolicy < Struct.new(:user, :avatar); end +end