pundit 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Pundit
2
2
 
3
+ [![Build Status](https://secure.travis-ci.org/elabs/pundit.png?branch=master)](https://travis-ci.org/elabs/pundit)
4
+
3
5
  Pundit provides a set of helpers which guide you in leveraging regular Ruby
4
6
  classes and object oriented design patterns to build a simple, robust and
5
7
  scaleable authorization system.
@@ -193,6 +195,19 @@ You can, and are encouraged to, use this method in views:
193
195
  <% end %>
194
196
  ```
195
197
 
198
+ ## Manually specifying policy classes
199
+
200
+ Sometimes you might want to explicitly declare which policy to use for a given
201
+ class, instead of letting Pundit infer it. This can be done like so:
202
+
203
+ ``` ruby
204
+ class Post
205
+ def self.policy_class
206
+ PostablePolicyClass
207
+ end
208
+ end
209
+ ```
210
+
196
211
  ## Just plain old Ruby
197
212
 
198
213
  As you can see, Pundit doesn't do anything you couldn't have easily done
@@ -230,7 +245,7 @@ got through. This way you can fail more gracefully.
230
245
  ``` ruby
231
246
  class ApplicationPolicy
232
247
  def initialize(user, record)
233
- raise Pundit::NotAuthorized, "must be logged in" unless user
248
+ raise Pundit::NotAuthorizedError, "must be logged in" unless user
234
249
  @user = user
235
250
  @record = record
236
251
  end
@@ -6,44 +6,47 @@ module Pundit
6
6
  @object = object
7
7
  end
8
8
 
9
- def name
10
- if object.respond_to?(:model_name)
11
- object.model_name.to_s
12
- elsif object.class.respond_to?(:model_name)
13
- object.class.model_name.to_s
14
- elsif object.is_a?(Class)
15
- object.to_s
16
- else
17
- object.class.to_s
18
- end
19
- end
20
-
21
9
  def scope
22
- scope_name.constantize
10
+ policy::Scope if policy
23
11
  rescue NameError
24
12
  nil
25
13
  end
26
14
 
27
15
  def policy
28
- policy_name.constantize
16
+ klass = find
17
+ klass = klass.constantize if klass.is_a?(String)
18
+ klass
29
19
  rescue NameError
30
20
  nil
31
21
  end
32
22
 
33
23
  def scope!
34
- scope or raise NotDefinedError, "unable to find scope #{scope_name} for #{object}"
24
+ scope or raise NotDefinedError, "unable to find scope #{find}::Scope for #{object}"
35
25
  end
36
26
 
37
27
  def policy!
38
- policy or raise NotDefinedError, "unable to find policy #{policy_name} for #{object}"
28
+ policy or raise NotDefinedError, "unable to find policy #{find} for #{object}"
39
29
  end
40
30
 
41
- def scope_name
42
- "#{name}Policy::Scope"
43
- end
31
+ private
44
32
 
45
- def policy_name
46
- "#{name}Policy"
33
+ def find
34
+ if object.respond_to?(:policy_class)
35
+ object.policy_class
36
+ elsif object.class.respond_to?(:policy_class)
37
+ object.class.policy_class
38
+ else
39
+ klass = if object.respond_to?(:model_name)
40
+ object.model_name
41
+ elsif object.class.respond_to?(:model_name)
42
+ object.class.model_name
43
+ elsif object.is_a?(Class)
44
+ object
45
+ else
46
+ object.class
47
+ end
48
+ "#{klass}Policy"
49
+ end
47
50
  end
48
51
  end
49
52
  end
@@ -1,3 +1,3 @@
1
1
  module Pundit
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -34,12 +34,34 @@ class Comment; extend ActiveModel::Naming; end
34
34
 
35
35
  class Article; end
36
36
 
37
+ class BlogPolicy < Struct.new(:user, :blog); end
38
+ class Blog; end
39
+ class ArtificialBlog < Blog
40
+ def self.policy_class
41
+ BlogPolicy
42
+ end
43
+ end
44
+ class ArticleTag
45
+ def self.policy_class
46
+ Struct.new(:user, :tag) do
47
+ def show?
48
+ true
49
+ end
50
+ def destroy?
51
+ false
52
+ end
53
+ end
54
+ end
55
+ end
56
+
37
57
  describe Pundit do
38
58
  let(:user) { stub }
39
59
  let(:post) { Post.new(user) }
40
60
  let(:comment) { Comment.new }
41
61
  let(:article) { Article.new }
42
62
  let(:controller) { stub(:current_user => user, :params => { :action => "update" }).tap { |c| c.extend(Pundit) } }
63
+ let(:artificial_blog) { ArtificialBlog.new }
64
+ let(:article_tag) { ArticleTag.new }
43
65
 
44
66
  describe ".policy_scope" do
45
67
  it "returns an instantiated policy scope given a plain model class" do
@@ -67,6 +89,10 @@ describe Pundit do
67
89
  it "throws an exception if the given policy scope can't be found" do
68
90
  expect { Pundit.policy_scope!(user, Article) }.to raise_error(Pundit::NotDefinedError)
69
91
  end
92
+
93
+ it "throws an exception if the given policy scope can't be found" do
94
+ expect { Pundit.policy_scope!(user, ArticleTag) }.to raise_error(Pundit::NotDefinedError)
95
+ end
70
96
  end
71
97
 
72
98
  describe ".policy" do
@@ -98,6 +124,32 @@ describe Pundit do
98
124
  Pundit.policy(user, article).should be_nil
99
125
  Pundit.policy(user, Article).should be_nil
100
126
  end
127
+
128
+ describe "with .policy_class set on the model" do
129
+ it "returns an instantiated policy given a plain model instance" do
130
+ policy = Pundit.policy(user, artificial_blog)
131
+ policy.user.should == user
132
+ policy.blog.should == artificial_blog
133
+ end
134
+
135
+ it "returns an instantiated policy given a plain model class" do
136
+ policy = Pundit.policy(user, ArtificialBlog)
137
+ policy.user.should == user
138
+ policy.blog.should == ArtificialBlog
139
+ end
140
+
141
+ it "returns an instantiated policy given a plain model instance providing an anonymous class" do
142
+ policy = Pundit.policy(user, article_tag)
143
+ policy.user.should == user
144
+ policy.tag.should == article_tag
145
+ end
146
+
147
+ it "returns an instantiated policy given a plain model class providing an anonymous class" do
148
+ policy = Pundit.policy(user, ArticleTag)
149
+ policy.user.should == user
150
+ policy.tag.should == ArticleTag
151
+ end
152
+ end
101
153
  end
102
154
 
103
155
  describe ".policy!" do
@@ -152,6 +204,11 @@ describe Pundit do
152
204
  expect { controller.authorize(post, :destroy?) }.to raise_error(Pundit::NotAuthorizedError)
153
205
  end
154
206
 
207
+ it "works with anonymous class policies" do
208
+ controller.authorize(article_tag, :show?).should be_true
209
+ expect { controller.authorize(article_tag, :destroy?) }.to raise_error(Pundit::NotAuthorizedError)
210
+ end
211
+
155
212
  it "raises an error when the permission check fails" do
156
213
  expect { controller.authorize(Post.new) }.to raise_error(Pundit::NotAuthorizedError)
157
214
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pundit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,88 +10,88 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2012-11-19 00:00:00.000000000 Z
13
+ date: 2013-01-08 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rails
17
+ prerelease: false
17
18
  requirement: !ruby/object:Gem::Requirement
18
- none: false
19
19
  requirements:
20
20
  - - ~>
21
21
  - !ruby/object:Gem::Version
22
22
  version: '3.0'
23
+ none: false
23
24
  type: :runtime
24
- prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
- none: false
27
26
  requirements:
28
27
  - - ~>
29
28
  - !ruby/object:Gem::Version
30
29
  version: '3.0'
30
+ none: false
31
31
  - !ruby/object:Gem::Dependency
32
32
  name: rspec
33
+ prerelease: false
33
34
  requirement: !ruby/object:Gem::Requirement
34
- none: false
35
35
  requirements:
36
36
  - - ~>
37
37
  - !ruby/object:Gem::Version
38
38
  version: '2.0'
39
+ none: false
39
40
  type: :development
40
- prerelease: false
41
41
  version_requirements: !ruby/object:Gem::Requirement
42
- none: false
43
42
  requirements:
44
43
  - - ~>
45
44
  - !ruby/object:Gem::Version
46
45
  version: '2.0'
46
+ none: false
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: pry
49
+ prerelease: false
49
50
  requirement: !ruby/object:Gem::Requirement
50
- none: false
51
51
  requirements:
52
52
  - - ! '>='
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
+ none: false
55
56
  type: :development
56
- prerelease: false
57
57
  version_requirements: !ruby/object:Gem::Requirement
58
- none: false
59
58
  requirements:
60
59
  - - ! '>='
61
60
  - !ruby/object:Gem::Version
62
61
  version: '0'
62
+ none: false
63
63
  - !ruby/object:Gem::Dependency
64
64
  name: rake
65
+ prerelease: false
65
66
  requirement: !ruby/object:Gem::Requirement
66
- none: false
67
67
  requirements:
68
68
  - - ! '>='
69
69
  - !ruby/object:Gem::Version
70
70
  version: '0'
71
+ none: false
71
72
  type: :development
72
- prerelease: false
73
73
  version_requirements: !ruby/object:Gem::Requirement
74
- none: false
75
74
  requirements:
76
75
  - - ! '>='
77
76
  - !ruby/object:Gem::Version
78
77
  version: '0'
78
+ none: false
79
79
  - !ruby/object:Gem::Dependency
80
80
  name: yard
81
+ prerelease: false
81
82
  requirement: !ruby/object:Gem::Requirement
82
- none: false
83
83
  requirements:
84
84
  - - ! '>='
85
85
  - !ruby/object:Gem::Version
86
86
  version: '0'
87
+ none: false
87
88
  type: :development
88
- prerelease: false
89
89
  version_requirements: !ruby/object:Gem::Requirement
90
- none: false
91
90
  requirements:
92
91
  - - ! '>='
93
92
  - !ruby/object:Gem::Version
94
93
  version: '0'
94
+ none: false
95
95
  description: Object oriented authorization for Rails applications
96
96
  email:
97
97
  - jonas.nicklas@gmail.com
@@ -125,17 +125,17 @@ rdoc_options: []
125
125
  require_paths:
126
126
  - lib
127
127
  required_ruby_version: !ruby/object:Gem::Requirement
128
- none: false
129
128
  requirements:
130
129
  - - ! '>='
131
130
  - !ruby/object:Gem::Version
132
131
  version: '0'
133
- required_rubygems_version: !ruby/object:Gem::Requirement
134
132
  none: false
133
+ required_rubygems_version: !ruby/object:Gem::Requirement
135
134
  requirements:
136
135
  - - ! '>='
137
136
  - !ruby/object:Gem::Version
138
137
  version: '0'
138
+ none: false
139
139
  requirements: []
140
140
  rubyforge_project:
141
141
  rubygems_version: 1.8.24