1
0
Fork 0
mirror of https://github.com/varvet/pundit.git synced 2022-11-09 12:30:11 -05:00

Improvements on .policy_class support

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.
This commit is contained in:
Jason Daly 2012-12-13 18:20:12 -05:00
parent 4fc13620ee
commit e65159f26b
2 changed files with 79 additions and 36 deletions

View file

@ -6,30 +6,16 @@ module Pundit
@object = object
end
def name
if object.respond_to?(:policy_class)
object.policy_class.name.gsub(/Policy$/, '')
elsif object.class.respond_to?(:policy_class)
object.class.policy_class.name.gsub(/Policy$/, '')
elsif object.respond_to?(:model_name)
object.model_name.to_s
elsif object.class.respond_to?(:model_name)
object.class.model_name.to_s
elsif object.is_a?(Class)
object.to_s
else
object.class.to_s
end
end
def scope
scope_name.constantize
policy::Scope
rescue NameError
nil
end
def policy
policy_name.constantize
klass = policy_evaluator
klass = klass.constantize if klass.is_a?(String)
klass
rescue NameError
nil
end
@ -42,12 +28,33 @@ module Pundit
policy or raise NotDefinedError, "unable to find policy #{policy_name} for #{object}"
end
def scope_name
"#{name}Policy::Scope"
end
private
def policy_evaluator
if object.respond_to?(:policy_class)
return object.policy_class
elsif object.class.respond_to?(:policy_class)
return object.class.policy_class
elsif object.respond_to?(:model_name)
klass = object.model_name
elsif object.class.respond_to?(:model_name)
klass = object.class.model_name
elsif object.is_a?(Class)
klass = object
else
klass = object.class
end
def policy_name
"#{name}Policy"
end
"#{klass}Policy"
end
def scope_name
"#{policy_name}::Scope"
end
def policy_name
name = policy_evaluator
return name.name unless name.is_a?(String)
name
end
end
end

View file

@ -41,6 +41,18 @@ class ArtificialBlog < Blog
BlogPolicy
end
end
class ArticleTag
def self.policy_class
Struct.new(:user, :tag) do
def show?
true
end
def destroy?
false
end
end
end
end
describe Pundit do
let(:user) { stub }
@ -49,6 +61,7 @@ describe Pundit do
let(:article) { Article.new }
let(:controller) { stub(:current_user => user, :params => { :action => "update" }).tap { |c| c.extend(Pundit) } }
let(:artificial_blog) { ArtificialBlog.new }
let(:article_tag) { ArticleTag.new }
describe ".policy_scope" do
it "returns an instantiated policy scope given a plain model class" do
@ -76,6 +89,10 @@ describe Pundit do
it "throws an exception if the given policy scope can't be found" do
expect { Pundit.policy_scope!(user, Article) }.to raise_error(Pundit::NotDefinedError)
end
it "throws an exception if the given policy scope can't be found" do
expect { Pundit.policy_scope!(user, ArticleTag) }.to raise_error(Pundit::NotDefinedError)
end
end
describe ".policy" do
@ -85,18 +102,6 @@ describe Pundit do
policy.post.should == post
end
it "returns an instantiated policy given a plain model instance with policy_class class method set" do
policy = Pundit.policy(user, artificial_blog)
policy.user.should == user
policy.blog.should == artificial_blog
end
it "returns an instantiated policy given a plain model class with policy_class class method set" do
policy = Pundit.policy(user, ArtificialBlog)
policy.user.should == user
policy.blog.should == ArtificialBlog
end
it "returns an instantiated policy given an active model instance" do
policy = Pundit.policy(user, comment)
policy.user.should == user
@ -119,6 +124,32 @@ describe Pundit do
Pundit.policy(user, article).should be_nil
Pundit.policy(user, Article).should be_nil
end
describe "with .policy_class set on the model" do
it "returns an instantiated policy given a plain model instance" do
policy = Pundit.policy(user, artificial_blog)
policy.user.should == user
policy.blog.should == artificial_blog
end
it "returns an instantiated policy given a plain model class" do
policy = Pundit.policy(user, ArtificialBlog)
policy.user.should == user
policy.blog.should == ArtificialBlog
end
it "returns an instantiated policy given a plain model instance providing an anonymous class" do
policy = Pundit.policy(user, article_tag)
policy.user.should == user
policy.tag.should == article_tag
end
it "returns an instantiated policy given a plain model class providing an anonymous class" do
policy = Pundit.policy(user, ArticleTag)
policy.user.should == user
policy.tag.should == ArticleTag
end
end
end
describe ".policy!" do
@ -173,6 +204,11 @@ describe Pundit do
expect { controller.authorize(post, :destroy?) }.to raise_error(Pundit::NotAuthorizedError)
end
it "works with anonymous class policies" do
controller.authorize(article_tag, :show?).should be_true
expect { controller.authorize(article_tag, :destroy?) }.to raise_error(Pundit::NotAuthorizedError)
end
it "raises an error when the permission check fails" do
expect { controller.authorize(Post.new) }.to raise_error(Pundit::NotAuthorizedError)
end