pundit_custom_errors 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6465ad6640e3f2d62f2542719caa33982043014f
4
+ data.tar.gz: 4755a3988e8db347ad1bc0a5a94314f0455fe086
5
+ SHA512:
6
+ metadata.gz: 3a43ad7336a3da385e459d460615d78f696681beeca07d4d15fd0da2d62f1f8dc05ba1c9174dbc93b0d75602a9aa82f8bf0facc9b0673c749d8fc9f24f02150b
7
+ data.tar.gz: a846d4fe2b0da3d0e777f71e09afce92fc98183ac9d8642a4ec12eca7a59e92f69979bbdff48d5f66a9cb79b5bc52f057a2224b4e14b31f7316737f53b14d0b5
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+ *.gem
@@ -0,0 +1 @@
1
+ 2.1.2
@@ -0,0 +1,3 @@
1
+ rvm:
2
+ - 2.1.2
3
+ script: rspec spec
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in pundit_custom_errors.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Luis Daher
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,108 @@
1
+ # PunditCustomErrors
2
+ [![Build Status](https://travis-ci.org/luisdaher/pundit_custom_errors.svg)](https://travis-ci.org/luisdaher/pundit_custom_errors) [![Code Climate](https://codeclimate.com/github/luisdaher/pundit_custom_errors/badges/gpa.svg)](https://codeclimate.com/github/luisdaher/pundit_custom_errors) [![Inline docs](http://inch-ci.org/github/luisdaher/pundit_custom_errors.svg?branch=master)](http://inch-ci.org/github/luisdaher/pundit_custom_errors)
3
+
4
+ `pundit_custom_errors` is an extension for the [Pundit gem](https://github.com/elabs/pundit) that enables the creation of custom error messages. This adds more flexibility to retrieve different kinds of messages in the same validation method, according to the nature of the error. As for the default error message, it is also set up to generate them by using a localization file (if existent).
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ ```ruby
11
+ gem 'pundit_custom_errors'
12
+ ```
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install pundit_custom_errors
21
+
22
+ ## Getting Started
23
+
24
+ (Note: since this gem is intended to extend [Pundit's](https://github.com/elabs/pundit) capabilities, it is assumed that you're already familiar with it. If not, It is highly recommended that you take a look at [Pundit's documentation](https://github.com/elabs/pundit#pundit) first.)
25
+
26
+ For this example, let's suppose there's already a standard `Policy` class for an object (`Post`, in this case), called `PostPolicy`, containing a validation for the `show` action. It would look like this:
27
+
28
+ ```ruby
29
+ class PostPolicy
30
+ def show?
31
+ # dummy validation code for the 'show' action
32
+ false
33
+ end
34
+ end
35
+ ```
36
+
37
+ Since the `show?` method is returning `false`, this example will always throw a `Pundit::NotAuthorizedError`, with Pundit's default error message inside. As said before, this behavior can be changed by either setting a message in an attribute inside the `Policy` class or creating default messages for the actions' validation methods inside a localization YAML file. Both approaches are explained below.
38
+
39
+ ### Setting a message in an attribute inside the `Policy` class
40
+
41
+ In order to set custom messages, the first thing to be done is to create the `error_message` attribute with `attr_accessor` permissions inside the desired `Policy` class. The `PostPolicy` class will look like this:
42
+
43
+ ```ruby
44
+ class PostPolicy
45
+ attr_accessor :error_message
46
+
47
+ def show?
48
+ # dummy validation code for the 'show' action
49
+ false
50
+ end
51
+ end
52
+ ```
53
+
54
+ By putting that attribute inside a class, every time a validation method returns false, `Pundit` will try to use the `error_message`. If there's something set there (like the example below), `Pundit` will use it as the error message.
55
+
56
+ ```ruby
57
+ class PostPolicy
58
+ attr_accessor :error_message
59
+
60
+ def show?
61
+ @error_message = "You're not allowed to see this. Better luck next time!"
62
+
63
+ # dummy validation code for the 'show' action
64
+ false
65
+ end
66
+ end
67
+ ```
68
+
69
+ As the message is set, every time the `show?` method returns false, the message present inside `error_message` will be put inside the `Pundit::NotAuthorizedError` instance.
70
+
71
+ ### Creating default error messages inside a YAML file
72
+
73
+ #### Creating the YAML file
74
+
75
+ By running the command:
76
+
77
+ $ rails generate pundit_custom_errors:initialize
78
+
79
+ A file called `pundit_custom_errors.en.yml` will be generated inside the `config/locales` folder. It contains the default message, used if there's no messages for the given controller/action validation.
80
+
81
+ #### Creating error messages in the localization file
82
+
83
+ The `policy` keys must be the snake case representation of the given policy classes, containing action names as keys (e.g: `show?` and the message as value). Also, the `policy` hashes must be inside the `pundit` hash, inside the `en` (or the desired language abbreviation) hash.
84
+
85
+ In short, the YAML file structure should be similar as the example below:
86
+
87
+ ```yaml
88
+ en:
89
+ pundit:
90
+ default: "You have requested a page that you do not have access to."
91
+ post_policy:
92
+ show?: "You're not allowed to see this."
93
+ ```
94
+
95
+ ## Message hierarchy
96
+
97
+ 1. The gem will use the message present in the `error_message` attribute, that should be part of the given `Policy` class.
98
+ 2. If there's no error message (or even the attribute, if `PunditCustomErrors::Policy` isn't being extended), it will use the message for the given action validation, present in the YAML file.
99
+ 3. If there's no message for the given action validation in the YAML, it will use the YAML's default message inside `pundit` hash.
100
+ 4. If there's no YAML default message, it will use the hardcoded message, behaving the same way as Pundit does today.
101
+
102
+ ## Contributing
103
+
104
+ 1. Fork it ( https://github.com/luisdaher/pundit_custom_errors/fork )
105
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
106
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
107
+ 4. Push to the branch (`git push origin my-new-feature`)
108
+ 5. Create a new Pull Request
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,18 @@
1
+ module PunditCustomErrors
2
+ module Generators
3
+ # Generates the localization strings YAML file in english
4
+ class InitializeGenerator < ::Rails::Generators::Base
5
+ desc 'Copy pundit_custom_errors.yml file to application config directory.'
6
+
7
+ def self.source_root
8
+ @source_root ||= File.expand_path(
9
+ File.join(File.dirname(__FILE__), 'templates')
10
+ )
11
+ end
12
+
13
+ def copy_config_file
14
+ template 'config/locales/pundit_custom_errors.en.yml'
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,3 @@
1
+ en:
2
+ pundit:
3
+ default: "You have requested a page that you do not have access to."
@@ -0,0 +1,7 @@
1
+ require 'pundit_custom_errors/version'
2
+ require 'pundit_custom_errors/authorization'
3
+
4
+ # The 'PunditCustomErrors::Authorization' module is being prepended here.
5
+ module Pundit
6
+ prepend PunditCustomErrors::Authorization
7
+ end
@@ -0,0 +1,41 @@
1
+ module PunditCustomErrors
2
+ # Module created to override Pundit's 'authorize' function. It enables Pundit
3
+ # to use the 'error_message' attribute (if existent) inside a Policy object,
4
+ # displaying the given error message instead of a default error message.
5
+ module Authorization
6
+ def authorize(record, query = nil)
7
+ @_pundit_policy_authorized = true
8
+
9
+ query ||= params[:action].to_s + '?'
10
+ policy = policy(record)
11
+ unless policy.public_send(query)
12
+ fail generate_error_for(policy, query, record)
13
+ end
14
+
15
+ true
16
+ end
17
+
18
+ protected
19
+
20
+ def generate_error_for(policy, query, record)
21
+ if policy.respond_to? :error_message
22
+ message = policy.error_message
23
+ policy.error_message = nil
24
+ end
25
+
26
+ message ||= translate_error_message_for_query(query, policy)
27
+ message ||= "not allowed to #{query} this #{record}"
28
+
29
+ error = Pundit::NotAuthorizedError.new(message)
30
+
31
+ error.query, error.record, error.policy = query, record, policy
32
+ error
33
+ end
34
+
35
+ def translate_error_message_for_query(query, policy)
36
+ t("#{policy.class.to_s.underscore}.#{query}",
37
+ scope: 'pundit',
38
+ default: :default) if self.respond_to?(:t)
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,3 @@
1
+ module PunditCustomErrors
2
+ VERSION = '0.9.0'
3
+ end
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'pundit_custom_errors/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'pundit_custom_errors'
8
+ spec.version = PunditCustomErrors::VERSION
9
+ spec.authors = ['Luis Daher', 'Damien Wilson']
10
+ spec.email = ['luisotaviodaher@gmail.com', 'damien@mindglob.com']
11
+ spec.summary = 'Custom error messages for Pundit.'
12
+ spec.description = %(An extension for the Pundit gem that enables the
13
+ creation of custom error messages.)
14
+ spec.homepage = 'http://luisdaher.net'
15
+ spec.license = 'MIT'
16
+
17
+ spec.files = `git ls-files -z`.split("\x0")
18
+ spec.executables = spec.files.grep(%r{/^bin/}) { |f| File.basename(f) }
19
+ spec.test_files = spec.files.grep(%r{/^(test|spec|features)/})
20
+ spec.require_paths = ['lib']
21
+
22
+ spec.add_development_dependency 'bundler', '~> 1.6'
23
+ spec.add_development_dependency 'rake', '~> 10.0'
24
+ spec.add_development_dependency 'rspec', '~> 3.2', '>= 3.2.0'
25
+ spec.add_development_dependency 'pry', '~> 0.10', '>= 0.10.1'
26
+ spec.add_development_dependency 'rubocop', '~> 0.28', '>= 0.28.0'
27
+ spec.add_dependency 'pundit', '~> 0.3', '>= 0.3.0'
28
+ end
@@ -0,0 +1,157 @@
1
+ require 'spec_helper'
2
+
3
+ describe PunditCustomErrors::Authorization do
4
+ # Pundit Mock class
5
+ class PunditMock
6
+ prepend PunditCustomErrors::Authorization
7
+ def policy(_arg = nil)
8
+ @policy ||= DummyPolicy.new
9
+ end
10
+ end
11
+
12
+ # Dummy Policy class for testing purposes
13
+ class DummyPolicy
14
+ end
15
+
16
+ describe '.authorize' do
17
+ let(:pundit_mock) { PunditMock.new }
18
+
19
+ context "when the user isn't authorized" do
20
+ context "and it's a single error" do
21
+ before :each do
22
+ expect_any_instance_of(DummyPolicy).to(
23
+ receive(:show?).and_return(false)
24
+ )
25
+ end
26
+
27
+ it 'raises an error' do
28
+ expect { pundit_mock.authorize(Class.new, :show?) }.to raise_error
29
+ end
30
+
31
+ context 'and a custom error message has been set' do
32
+ it 'returns the error with the previously set error message' do
33
+ expect_any_instance_of(DummyPolicy).to(
34
+ receive(:error_message).and_return('error')
35
+ )
36
+ expect_any_instance_of(DummyPolicy).to(
37
+ receive(:error_message=).and_return(nil)
38
+ )
39
+ expect { pundit_mock.authorize(Class.new, :show?) }.to(
40
+ raise_error(
41
+ Pundit::NotAuthorizedError,
42
+ 'error'
43
+ )
44
+ )
45
+ end
46
+ end
47
+ end
48
+
49
+ context 'and there is more than one error' do
50
+ # Dummy Policy class for testing purposes
51
+ class DummyPolicy
52
+ attr_accessor :error_message
53
+
54
+ def index?
55
+ @error_message = 'error'
56
+ false
57
+ end
58
+
59
+ def show?
60
+ false
61
+ end
62
+
63
+ def update?
64
+ @error_message = 'update error'
65
+ false
66
+ end
67
+ end
68
+
69
+ context "and the other error doesn't contain a custom message" do
70
+ it 'shows the default error message' do
71
+ class_instance = Class.new
72
+
73
+ expect { pundit_mock.authorize(class_instance, :index?) }.to(
74
+ raise_error(
75
+ Pundit::NotAuthorizedError,
76
+ 'error'
77
+ )
78
+ )
79
+
80
+ expect { pundit_mock.authorize(class_instance, :show?) }.to(
81
+ raise_error(
82
+ Pundit::NotAuthorizedError,
83
+ "not allowed to show? this #{class_instance}"
84
+ )
85
+ )
86
+ end
87
+ end
88
+
89
+ context 'and the other error also contains a custom message' do
90
+ it "doesn't show the previous error message" do
91
+ class_instance = Class.new
92
+
93
+ expect { pundit_mock.authorize(class_instance, :index?) }.to(
94
+ raise_error(
95
+ Pundit::NotAuthorizedError,
96
+ 'error'
97
+ )
98
+ )
99
+
100
+ expect { pundit_mock.authorize(class_instance, :update?) }.to(
101
+ raise_error(
102
+ Pundit::NotAuthorizedError,
103
+ 'update error'
104
+ )
105
+ )
106
+ end
107
+ end
108
+ end
109
+
110
+ context "and there's a translated message but no custom message" do
111
+ before :each do
112
+ expect(pundit_mock).to(
113
+ receive(:t)
114
+ .with(
115
+ "#{pundit_mock.policy.class.to_s.underscore}.show?",
116
+ scope: 'pundit',
117
+ default: :default
118
+ )
119
+ .and_return('translated error message')
120
+ )
121
+ end
122
+
123
+ it 'returns the error with the translated message' do
124
+ expect { pundit_mock.authorize(Class.new, :show?) }.to(
125
+ raise_error(
126
+ Pundit::NotAuthorizedError,
127
+ 'translated error message'
128
+ )
129
+ )
130
+ end
131
+ end
132
+
133
+ context 'and no message is found' do
134
+ it 'returns the error with the default message' do
135
+ record = Class.new
136
+ expect { pundit_mock.authorize(record, :show?) }.to(
137
+ raise_error(
138
+ Pundit::NotAuthorizedError,
139
+ "not allowed to show? this #{record}"
140
+ )
141
+ )
142
+ end
143
+ end
144
+ end
145
+
146
+ context 'when the user is authorized' do
147
+ before :each do
148
+ expect_any_instance_of(DummyPolicy).to(
149
+ receive(:show?).and_return(true)
150
+ )
151
+ end
152
+ it "doesn't raise an error" do
153
+ expect { pundit_mock.authorize(Class.new, :show?) }.not_to raise_error
154
+ end
155
+ end
156
+ end
157
+ end
@@ -0,0 +1,11 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ require 'pundit_custom_errors'
4
+ require 'pundit'
5
+ require 'pry'
6
+
7
+ Bundler.setup(:default, :test, :development)
8
+
9
+ RSpec.configure do |config|
10
+ config.mock_with :rspec
11
+ end
metadata ADDED
@@ -0,0 +1,172 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pundit_custom_errors
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.0
5
+ platform: ruby
6
+ authors:
7
+ - Luis Daher
8
+ - Damien Wilson
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2015-03-16 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '1.6'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '1.6'
28
+ - !ruby/object:Gem::Dependency
29
+ name: rake
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '10.0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '10.0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: rspec
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '3.2'
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: 3.2.0
52
+ type: :development
53
+ prerelease: false
54
+ version_requirements: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - "~>"
57
+ - !ruby/object:Gem::Version
58
+ version: '3.2'
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 3.2.0
62
+ - !ruby/object:Gem::Dependency
63
+ name: pry
64
+ requirement: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.10'
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: 0.10.1
72
+ type: :development
73
+ prerelease: false
74
+ version_requirements: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - "~>"
77
+ - !ruby/object:Gem::Version
78
+ version: '0.10'
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: 0.10.1
82
+ - !ruby/object:Gem::Dependency
83
+ name: rubocop
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '0.28'
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: 0.28.0
92
+ type: :development
93
+ prerelease: false
94
+ version_requirements: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - "~>"
97
+ - !ruby/object:Gem::Version
98
+ version: '0.28'
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: 0.28.0
102
+ - !ruby/object:Gem::Dependency
103
+ name: pundit
104
+ requirement: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - "~>"
107
+ - !ruby/object:Gem::Version
108
+ version: '0.3'
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: 0.3.0
112
+ type: :runtime
113
+ prerelease: false
114
+ version_requirements: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - "~>"
117
+ - !ruby/object:Gem::Version
118
+ version: '0.3'
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ version: 0.3.0
122
+ description: |-
123
+ An extension for the Pundit gem that enables the
124
+ creation of custom error messages.
125
+ email:
126
+ - luisotaviodaher@gmail.com
127
+ - damien@mindglob.com
128
+ executables: []
129
+ extensions: []
130
+ extra_rdoc_files: []
131
+ files:
132
+ - ".gitignore"
133
+ - ".ruby-version"
134
+ - ".travis.yml"
135
+ - Gemfile
136
+ - LICENSE.txt
137
+ - README.md
138
+ - Rakefile
139
+ - lib/generators/pundit_custom_errors/initialize/initialize_generator.rb
140
+ - lib/generators/pundit_custom_errors/initialize/templates/config/locales/pundit_custom_errors.en.yml
141
+ - lib/pundit_custom_errors.rb
142
+ - lib/pundit_custom_errors/authorization.rb
143
+ - lib/pundit_custom_errors/version.rb
144
+ - pundit_custom_errors.gemspec
145
+ - spec/pundit_custom_errors/authorization_spec.rb
146
+ - spec/spec_helper.rb
147
+ homepage: http://luisdaher.net
148
+ licenses:
149
+ - MIT
150
+ metadata: {}
151
+ post_install_message:
152
+ rdoc_options: []
153
+ require_paths:
154
+ - lib
155
+ required_ruby_version: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ required_rubygems_version: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - ">="
163
+ - !ruby/object:Gem::Version
164
+ version: '0'
165
+ requirements: []
166
+ rubyforge_project:
167
+ rubygems_version: 2.2.2
168
+ signing_key:
169
+ specification_version: 4
170
+ summary: Custom error messages for Pundit.
171
+ test_files: []
172
+ has_rdoc: