rspec-authorization 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d32c0f50faa40a57496f87a21d5ec3cdbf538f01
4
- data.tar.gz: 4ad22903e43240efdf66a025c70d9812bb97c8a2
3
+ metadata.gz: 79650ca2e93b08c5ebd9008fa9e6f51e59d5245c
4
+ data.tar.gz: 64f6364b28ef24e98d203b764165201ac2c32a33
5
5
  SHA512:
6
- metadata.gz: 68c08236ab63ec53595788a07de8256941934bf1993727d2edf002ab71efd6d6681060feb64ec4e96748a22a7d5457d4cf960d132a5d1d99744e53e7c8471da5
7
- data.tar.gz: 342c1b6aa908f713b56eba516821f47af14d78e0659c8ff8f8574e11143570d1940d2f31d2a30ace4e05c4f65f02d8d4ee5555fb947cd47b30dd0393ed715840
6
+ metadata.gz: f28d44b4dec4e86ade6980070bebcbfdb0b35a9daded58db14e539c086ba380959ab81aee49a09e30839cfb1d2fcc16138c05ebc4588d1b6ea9cde4d00971262
7
+ data.tar.gz: f026a6b7e065e16d10e53bad7bfdaf66838abe3952cf421739d113b3334ba76bc077529b2f5d6788f56491fa3e3aa32520f5db42f123b63e1c20ed4652129da8
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.4
4
+ before_script:
5
+ - bundle exec rake setup
data/.yardopts ADDED
@@ -0,0 +1,3 @@
1
+ -
2
+ HISTORY.md
3
+ LICENSE.txt
data/Gemfile CHANGED
@@ -8,7 +8,14 @@ group :development, :test do
8
8
  gem 'guard-rspec'
9
9
  gem 'pry'
10
10
  gem 'terminal-notifier-guard' if `uname` =~ /Darwin/
11
+ gem "codeclimate-test-reporter", require: nil
11
12
 
12
13
  gem 'jquery-rails'
13
14
  gem 'turbolinks'
14
15
  end
16
+
17
+ group :docs do
18
+ gem "inch"
19
+ gem "rdoc"
20
+ gem "yard"
21
+ end
data/Guardfile CHANGED
@@ -4,6 +4,7 @@ guard :rspec, cmd: 'bundle exec rspec' do
4
4
  watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
5
5
  watch(%r{^tools/(.+)\.rb$}) { |m| "spec/tools/#{m[1]}_spec.rb" }
6
6
 
7
- watch(%r{^controllers/(.+)\.rb$}) { |m| "spec/controllers/#{m[1]}_spec.rb" }
7
+ watch(%r{^lib/.+/matchers/.+\.rb$}) { "spec/controllers" }
8
+ watch(%r{^controllers/(.+)\.rb$}) { |m| "spec/controllers/#{m[1]}_spec.rb" }
8
9
  end
9
10
 
data/HISTORY.md ADDED
@@ -0,0 +1,32 @@
1
+
2
+ [v0.0.2 / 2014-11-11] (https://github.com/hendrauzia/rspec-authorization/tree/v0.0.2)
3
+ =====================
4
+
5
+ * Add RESTful methods matcher chaining
6
+ * Add object, callback and controller action stub
7
+ * Add documentation to have_permission_for matcher
8
+ * Add request documentation
9
+ * Refactor and add documentation to example
10
+ * Refactor and add documentation to example group
11
+ * Add documentation to gem requirement
12
+ * Add documentation to route
13
+ * Add yard as documentation generator
14
+ * Add documentation badge
15
+ * Add coverage, dependencies and security badges
16
+ * Fix rails test app spec
17
+ * Fix travis build
18
+ * Add travis badge and config
19
+ * Add gem version badge and usage
20
+
21
+ [v0.0.1 / 2014-11-05] (https://github.com/hendrauzia/rspec-authorization/tree/v0.0.1)
22
+ =====================
23
+
24
+ * Add have_permission_for matcher for restful actions
25
+ * Add rails test app template
26
+ * Add clean and reset rake task
27
+ * Update dependency to ruby 2.1.4
28
+ * Add rails test app for spec to run against
29
+ * Add rspec-rails for specs
30
+ * Add rspec-authorization gem scaffold using bundler
31
+ * Initial commit
32
+
data/README.md CHANGED
@@ -1,22 +1,75 @@
1
- # Rspec::Authorization
1
+ # RSpec::Authorization
2
2
 
3
- RSpec matcher for declarative_authorization. A neat way of asserting declarative_authorization's rules inside controller using RSpec matcher.
3
+ [![GitHub](http://img.shields.io/badge/github-hendrauzia/rspec--authorization-blue.svg)](http://github.com/hendrauzia/rspec-authorization)
4
+ [![Documentation](http://img.shields.io/badge/docs-rdoc.info-blue.svg)](http://rubydoc.org/github/hendrauzia/rspec-authorization)
5
+
6
+ [![Gem Version](https://badge.fury.io/rb/rspec-authorization.svg)](http://badge.fury.io/rb/rspec-authorization)
7
+ [![Build Status](https://travis-ci.org/hendrauzia/rspec-authorization.svg)](https://travis-ci.org/hendrauzia/rspec-authorization)
8
+ [![Code Climate](https://codeclimate.com/github/hendrauzia/rspec-authorization/badges/gpa.svg)](https://codeclimate.com/github/hendrauzia/rspec-authorization)
9
+ [![Test Coverage](https://codeclimate.com/github/hendrauzia/rspec-authorization/badges/coverage.svg)](https://codeclimate.com/github/hendrauzia/rspec-authorization)
10
+ [![Dependency Status](https://gemnasium.com/hendrauzia/rspec-authorization.svg)](https://gemnasium.com/hendrauzia/rspec-authorization)
11
+ [![security](https://hakiri.io/github/hendrauzia/rspec-authorization/master.svg)](https://hakiri.io/github/hendrauzia/rspec-authorization/master)
12
+ [![Inline docs](http://inch-ci.org/github/hendrauzia/rspec-authorization.svg?branch=master)](http://inch-ci.org/github/hendrauzia/rspec-authorization)
13
+
14
+ RSpec matcher for declarative_authorization. A neat way of asserting
15
+ declarative_authorization's rules inside controller using RSpec matcher.
4
16
 
5
17
  ## Installation
6
18
 
7
19
  Add this line to your application's Gemfile:
8
20
 
9
- ```ruby
10
- gem 'rspec-authorization'
11
- ```
21
+
22
+ gem 'rspec-authorization', group: :test
23
+
12
24
 
13
25
  And then execute:
14
26
 
15
- $ bundle
27
+ bundle
16
28
 
17
29
  Or install it yourself as:
18
30
 
19
- $ gem install rspec-authorization
31
+ gem install rspec-authorization
32
+
33
+ ## Requirement
34
+
35
+ Current development focus is as follows, future development may support other
36
+ dependencies, following are requirements for this gem:
37
+
38
+ - declarative_authorization 1.0.0.pre
39
+ - rails 4.x
40
+ - rspec-rails 3.x
41
+
42
+ ## Usage
43
+
44
+ In your controller spec:
45
+
46
+ describe ArticlesController do
47
+ it { is_expected.to have_permission_for(:a_role).to(:restful_action_name) }
48
+
49
+ it { is_expected.to have_permission_for(:writer).to(:index) }
50
+ it { is_expected.to have_permission_for(:writer).to(:show) }
51
+ it { is_expected.to have_permission_for(:writer).to(:new) }
52
+ it { is_expected.to have_permission_for(:writer).to(:create) }
53
+ it { is_expected.not_to have_permission_for(:writer).to(:edit) }
54
+ it { is_expected.not_to have_permission_for(:writer).to(:update) }
55
+ it { is_expected.not_to have_permission_for(:writer).to(:destroy) }
56
+ end
57
+
58
+ You can also use convenience RESTful methods matcher:
59
+
60
+ describe ArticlesController do
61
+ it { is_expected.to have_permission_for(:user).to_read }
62
+ it { is_expected.not_to have_permission_for(:user).to_create }
63
+ it { is_expected.not_to have_permission_for(:user).to_update }
64
+ it { is_expected.not_to have_permission_for(:user).to_delete }
65
+
66
+ it { is_expected.to have_permission_for(:writer).to_read }
67
+ it { is_expected.to have_permission_for(:writer).to_create }
68
+ it { is_expected.to have_permission_for(:writer).to_update }
69
+ it { is_expected.not_to have_permission_for(:writer).to_delete }
70
+
71
+ it { is_expected.to have_permission_for(:editor).to_manage }
72
+ end
20
73
 
21
74
  ## Contributing
22
75
 
@@ -1,27 +1,50 @@
1
1
  module RSpec::Authorization
2
2
  module Adapters
3
+ # Wrapper to generate and immediately run example from +RSpec::Core::Example+.
4
+ # The purpose of this class is to abstract the running of an example without
5
+ # unnecessary artifacts from an RSpec example, such as: reporter, generated
6
+ # description, context and expectation.
7
+ #
8
+ # The sole purpose of this class is to generate the minimum required component
9
+ # needed to create and run an example, for our matcher to run against. The trick
10
+ # to run the example without producing unnecessary artifacts is to trigger
11
+ # the example's before and after hook manually without running any expectations.
3
12
  class Example
4
- attr_reader :group, :example
13
+ # @return [Class] RSpec example group
14
+ attr_reader :group_target
15
+ # @return [RSpec::Core::Example] instance of RSpec example
16
+ attr_reader :target
5
17
 
6
- def initialize(group)
7
- @group = group
8
- @example = RSpec::Core::Example.new(group, "", {})
18
+ # Initialize example using RSpec example group. The RSpec example group can
19
+ # be retrieved using example group's target, consider the following example:
20
+ #
21
+ # group = ExampleGroup.new(ArticlesController)
22
+ # example = Example.new(group.target)
23
+ #
24
+ # @param group_target [Class] RSpec example group from +ExampleGroup#target+
25
+ # @see ExampleGroup#target
26
+ def initialize(group_target)
27
+ @group_target = group_target
28
+ @target = RSpec::Core::Example.new(group_target, "", {})
9
29
 
10
30
  set_example_group_instance
11
- end
12
-
13
- def run
14
31
  run_before_example
32
+ run_after_example
15
33
  end
16
34
 
17
35
  private
18
36
 
19
37
  def set_example_group_instance
20
- @example.instance_variable_set :@example_group_instance, group.new
38
+ target.instance_variable_set :@example_group_instance, group_target.new
21
39
  end
22
40
 
23
41
  def run_before_example
24
- @example.send :run_before_example
42
+ target.send :run_before_example
43
+ end
44
+
45
+ def run_after_example
46
+ target.example_group.hooks.run(:after, :example, target)
47
+ target.example_group_instance.teardown_mocks_for_rspec
25
48
  end
26
49
  end
27
50
  end
@@ -0,0 +1,78 @@
1
+ module RSpec::Authorization
2
+ module Adapters
3
+ # Wrapper to generate +RSpec::Core::ExampleGroup+. The example group
4
+ # includes +RSpec::Rails::ControllerExampleGroup+ to add behavior testing,
5
+ # which provides methods to test the controller, such as: +get+, +post+,
6
+ # +patch+, +put+ and +delete+.
7
+ #
8
+ # Consider the following example:
9
+ #
10
+ # group = ExampleGroup.new(ArticlesController)
11
+ # group.target # => RSpec::ExampleGroups::ArticlesController
12
+ #
13
+ # Instantiating the class for a second time produce a different result:
14
+ #
15
+ # group = ExampleGroup.new(ArticlesController)
16
+ # group.target # => RSpec::ExampleGroups::ArticlesController_2
17
+ class ExampleGroup
18
+ # @return [Class] a class namespaced from +RSPec::ExampleGroups+
19
+ attr_reader :target
20
+
21
+ # Initialize example group using controller's class.
22
+ #
23
+ # @param klass [Class] controller's class for an example group
24
+ def initialize(klass)
25
+ @target = RSpec::Core::ExampleGroup.describe(klass) do
26
+ include RSpec::Rails::ControllerExampleGroup
27
+ end
28
+ end
29
+
30
+ # Run example for our example group. The example is actualy has
31
+ # no example at all, it is simply a wrapper for +RSpec::Core::Example+,
32
+ # running an actual example would produce unnecessary artifacts, while all
33
+ # we need is a simple wrapper on controller behavior.
34
+ #
35
+ # @return [Example] example object that has been run
36
+ # @see Example
37
+ def run_example
38
+ Example.new(target)
39
+ end
40
+
41
+ # Add instruction to be run before our example. Instructions added will be
42
+ # run inside the context of our example group, consider the following
43
+ # example:
44
+ #
45
+ # before do
46
+ # get :index
47
+ # end
48
+ #
49
+ # The above statements behave exactly the same as RSpec +before+ method, and
50
+ # this method serve as a proxy to RSpec +before+ method call. That means
51
+ # everything that we can do inside +before+ method will be available inside
52
+ # +before+ to.
53
+ #
54
+ # @return [Array<RSpec::Core::Hooks::BeforeHook>] an array of before hook filter
55
+ def before(&block)
56
+ target.before(&block)
57
+ end
58
+
59
+ # Add instruction to be run after our example. Instructions added will be
60
+ # run inside the context of our example group, consider the following
61
+ # example:
62
+ #
63
+ # after do
64
+ # get :index
65
+ # end
66
+ #
67
+ # The above statements behave exactly the same as RSpec +after+ method, and
68
+ # this method serve as a proxy to RSpec +after+ method call. That means
69
+ # everything that we can do inside +after+ method will be available inside
70
+ # +after+ to.
71
+ #
72
+ # @return [Array<RSpec::Core::Hooks::AfterHook>] an array of after hook filter
73
+ def after(&block)
74
+ target.after(&block)
75
+ end
76
+ end
77
+ end
78
+ end
@@ -1,16 +1,86 @@
1
1
  module RSpec::Authorization
2
2
  module Adapters
3
+ # Create a request using wrapper around RSpec example. The request is made
4
+ # possible through wrapping the call around RSpec example, this approach
5
+ # is choosen because we need to get as close as possible to how the request
6
+ # is made inside RSpec.
7
+ #
8
+ # Once the request is created, it will immediately run and we can evaluate the
9
+ # response, consider the following example.
10
+ #
11
+ # request = Request.new(ArticlesController, :index, :user)
12
+ # request.response.status # => 200
13
+ #
14
+ # Since the request is actually creating a request to the provided controller,
15
+ # it will run everything inside the controller, and no stubbing is performed.
16
+ # Therefore exception inside controller will bubble up to the request and may
17
+ # cause unexpected result. That is with one exception, there is one exception,
18
+ # ActiveRecord::RecordNotFound is rescued and bypassed, therefore it will not
19
+ # affect the request.
3
20
  class Request
4
- attr_reader :klass, :action, :role, :group, :route, :response
21
+ # @return [Class] controller class name
22
+ attr_reader :klass
23
+ # @return [Symbol] controller action name
24
+ attr_reader :action
25
+ # @return [Symbol] role name from +config/authorization_rules.rb+
26
+ attr_reader :role
27
+ # @return [ExampleGroup] example group of the request
28
+ # @see ExampleGroup
29
+ attr_reader :group
30
+ # @return [Route] route object of the action
31
+ # @see Route
32
+ attr_reader :route
33
+ # @return [ActionController::TestResponse] response object of the request
34
+ attr_reader :response
5
35
 
36
+ # Create request object and immediately run the request. Inside initialization
37
+ # a lot of stuff get stubbed, it is to allow request to be run with assumptions,
38
+ # following are the assumptions composed as private method run inside the
39
+ # initializer:
40
+ #
41
+ # +stub_current_user+::
42
+ # The request is expected to run as a user with provided role. There is
43
+ # no user created whatsoever, it only creates a double with role_symbols
44
+ # defined and return it inside +current_user+.
45
+ #
46
+ # +stub_authorization_load_controller_object+::
47
+ # The request is expected to ignore declarative_authorization controller's
48
+ # object loading.
49
+ #
50
+ # +stub_authorization_load_object+::
51
+ # The request is expected to ignore declarative_authorization object
52
+ # loading. This is different from the above, and is expected to be called
53
+ # if controller object isn't loaded.
54
+ #
55
+ # +stub_callbacks+::
56
+ # The request is expected to ignore all callbacks defined inside the
57
+ # controller.
58
+ #
59
+ # +stub_action+::
60
+ # The request is expected to ignore all statements run inside the action
61
+ # and configured to render nothing instead.
62
+ #
63
+ # All of the above assumptions is expected to run only inside this request
64
+ # only, and not to change the behavior of the application outside of this
65
+ # request.
66
+ #
67
+ # @param klass [Class] controller class name to request from
68
+ # @param action [Symbol] action name, currently only support RESTful action
69
+ # @param role [Symbol] role name from +config/authorization_rules.rb+
6
70
  def initialize(klass, action, role)
7
71
  @klass, @action, @role = klass, action, role
8
- @group, @route = Group.new(klass), Route.new(action)
72
+ @group, @route = ExampleGroup.new(klass), Route.new(action)
73
+
74
+ @authorization_stubbed = false
9
75
 
10
76
  stub_current_user
77
+ stub_authorization_load_controller_object
78
+ stub_authorization_load_object
79
+ stub_callbacks
80
+ stub_action
11
81
 
12
82
  setup_response_retrieval
13
- dispatch
83
+ group.run_example
14
84
  end
15
85
 
16
86
  private
@@ -32,24 +102,69 @@ module RSpec::Authorization
32
102
  end
33
103
  end
34
104
 
35
- def setup_response_retrieval
36
- args = route
37
- setter = response_setter
105
+ def stub_authorization_load_controller_object
106
+ group.before do
107
+ allow(controller).to receive(:load_controller_object)
108
+ end
109
+ end
110
+
111
+ def stub_authorization_load_object
112
+ return if @authorization_stubbed
113
+ @authorization_stubbed = true
38
114
 
39
115
  group.before do
40
- begin
41
- send *args
42
- rescue ActiveRecord::RecordNotFound
43
- response.status = 404
116
+ Authorization::ControllerPermission.instance_eval do
117
+ alias_method :load_object_original, :load_object
118
+ define_method :load_object do |*args, &block|
119
+ begin
120
+ load_object_original(*args, &block)
121
+ rescue ActiveRecord::RecordNotFound
122
+ end
123
+ end
44
124
  end
125
+ end
45
126
 
46
- setter.call(response)
127
+ group.after do
128
+ Authorization::ControllerPermission.instance_eval do
129
+ remove_method :load_object
130
+
131
+ alias_method :load_object, :load_object_original
132
+ remove_method :load_object_original
133
+ end
47
134
  end
48
135
  end
49
136
 
50
- def dispatch
51
- example = Example.new(group)
52
- example.run
137
+ def stub_callbacks
138
+ group.before do
139
+ methods = controller._process_action_callbacks.map(&:filter).split(:filter_access_filter).last
140
+ controller.instance_eval do
141
+ methods.each do |method|
142
+ define_singleton_method method do |*args, &block|
143
+ end
144
+ end
145
+ end
146
+ end
147
+ end
148
+
149
+ def stub_action
150
+ args = route
151
+ group.before do
152
+ controller.instance_eval do
153
+ define_singleton_method args.action do |*args, &block|
154
+ render nothing: true
155
+ end
156
+ end
157
+ end
158
+ end
159
+
160
+ def setup_response_retrieval
161
+ args = route
162
+ setter = response_setter
163
+
164
+ group.before do
165
+ send *args
166
+ setter.call(response)
167
+ end
53
168
  end
54
169
  end
55
170
  end
@@ -1,8 +1,107 @@
1
1
  module RSpec::Authorization
2
2
  module Adapters
3
+ # Generate route that defines RESTful method using provided action. It is
4
+ # used primarily in creating a new request and infered automatically to an
5
+ # array using the defined +#to_a+ method.
6
+ #
7
+ # Route generation has 3 primary part, which are:
8
+ # - +verb+
9
+ # - +action+
10
+ # - +params+
11
+ #
12
+ # The first part, +verb+ refers to RESTful method, which is described in
13
+ # +DICTIONARIES+, +action+ refers to controller action name, and +params+
14
+ # refers to parameters used in a request. This object can be automatically
15
+ # inferred as an array.
16
+ #
17
+ # Creating a route will generate a route object:
18
+ #
19
+ # route = Route.new(:index)
20
+ # route # => #<Route:...>
21
+ #
22
+ # This will infer the object as an array:
23
+ #
24
+ # route = Route.new(:show)
25
+ # send *route # => [:get, :show, { id: 1 }]
26
+ #
27
+ # @see #to_a
3
28
  class Route
29
+ # @return [Symbol] returns route action name
4
30
  attr_reader :action
5
31
 
32
+ # Initializing a route requires a RESTful action name that the route want
33
+ # to generate. The action that get passed is assigned to +action+ attributes
34
+ # and is publicly accessible.
35
+ #
36
+ # route = Route.new(:index)
37
+ # route.action # => :index
38
+ #
39
+ # Currently +Route+ only support RESTful action, passing non-RESTful action
40
+ # name is possible, but will result in unexpected result.
41
+ #
42
+ # @param action [Symbol] RESTful action name
43
+ # @see #verb
44
+ def initialize(action)
45
+ @action = action
46
+ end
47
+
48
+ # This method is used to retrieve a dummy params that's used for an action.
49
+ # Tipically an action for a resource member requires a param, while action
50
+ # for resource collection doesn't need any param.
51
+ #
52
+ # This will return a dummy params:
53
+ #
54
+ # route = Route.new(:show)
55
+ # route.params # => { id: 1 }
56
+ #
57
+ # This will return nil for a collection resource action:
58
+ #
59
+ # route = Route.new(:index)
60
+ # route.params # => nil
61
+ #
62
+ # @return [Hash, nil] a dummy hash params
63
+ def params
64
+ PARAMS[action]
65
+ end
66
+
67
+ # Return verb used for the RESTful action. The verb uses +DICTIONARIES+ to
68
+ # retrieve a valid method for an action. This method intentionally throws
69
+ # error if a non-RESTful action is used.
70
+ #
71
+ # route = Route.new(:index)
72
+ # route.verb # => :get
73
+ #
74
+ # This will throw error:
75
+ #
76
+ # route = Route.new(:an_action)
77
+ # route.verb # => KeyError: key not found: :an_action
78
+ #
79
+ # @return [Symbol] RESTful verb to be used for an action
80
+ def verb
81
+ DICTIONARIES.fetch(action)
82
+ end
83
+
84
+ # This method is used to convert Route object into an array. This method also
85
+ # used to automatically infer +Route+ as an array on method that uses +to_a+,
86
+ # therefore we don't need to manually invoke +#to_a+ on every call that
87
+ # requires an array object.
88
+ #
89
+ # This will return an array:
90
+ #
91
+ # route = Route.new(:show)
92
+ # route.to_a # => [:get, :show, { id: 1 }]
93
+ #
94
+ # This will also return an array inferred automatically
95
+ #
96
+ # send *route # => [:get, :show, { id: 1 }]
97
+ #
98
+ # @return [Array] an array of verb, action and params
99
+ def to_a
100
+ [verb, action, params]
101
+ end
102
+
103
+ private
104
+
6
105
  DICTIONARIES = {
7
106
  index: :get,
8
107
  show: :get,
@@ -19,22 +118,6 @@ module RSpec::Authorization
19
118
  update: { id: 1 },
20
119
  destroy: { id: 1 }
21
120
  }
22
-
23
- def initialize(action)
24
- @action = action
25
- end
26
-
27
- def params
28
- PARAMS[action]
29
- end
30
-
31
- def verb
32
- DICTIONARIES.fetch(action)
33
- end
34
-
35
- def to_a
36
- [verb, action, params]
37
- end
38
121
  end
39
122
  end
40
123
  end
@@ -1,10 +1,10 @@
1
1
  require "rspec/authorization/adapters/example"
2
- require "rspec/authorization/adapters/group"
2
+ require "rspec/authorization/adapters/example_group"
3
3
  require "rspec/authorization/adapters/request"
4
4
  require "rspec/authorization/adapters/route"
5
5
 
6
6
  module RSpec::Authorization
7
- module Adapters
7
+ module Adapters # :nodoc:
8
8
  end
9
9
  end
10
10
 
@@ -1,43 +1,158 @@
1
1
  module RSpec::Authorization
2
2
  module Matchers
3
+ # Include this module to enable the +have_permission_for+ matcher inside RSpec.
4
+ # The following module is to be included only inside controller spec, this will
5
+ # add the capability to do a matcher against decalrative_authorization rules as
6
+ # follows:
7
+ #
8
+ # it { is_expected.to have_permission_for(:user).to(:index) }
9
+ #
10
+ # For your convenience, the following configuration has been enabled inside
11
+ # RSpec configuration.
12
+ #
13
+ # RSpec.configure do |config|
14
+ # config.include RSpec::Authorization::Matchers::HavePermissionFor, type: :controller
15
+ # end
3
16
  module HavePermissionFor
17
+ # Matcher to check permission of a role for a given controller in a spec. The
18
+ # following statement shows you how to use this matcher:
19
+ #
20
+ # describe ArticlesController do
21
+ # it { is_expected.to have_permission_for(:user).to(:index) }
22
+ # end
23
+ #
24
+ # Currently this matcher only support RESTful action check, to check the
25
+ # controller against +config/authorization_rules.rb+. Skipping the +#to+
26
+ # method will result in default action assigned as +:index+.
27
+ #
28
+ # Therefore, the following statement is exactly the same as above:
29
+ #
30
+ # it { is_expected.to have_permission_for(:user) }
31
+ #
32
+ # === RESTful methods
33
+ #
34
+ # For your convenience, there are 4 RESTful methods available to be chained
35
+ # from the matcher, which are:
36
+ #
37
+ # - +to_read+
38
+ # - +to_create+
39
+ # - +to_update+
40
+ # - +to_delete+
41
+ #
42
+ # Consider the following example:
43
+ #
44
+ # it { is_expected.to have_permission_for(:user).to_read }
45
+ # it { is_expected.to have_permission_for(:user).to_edit }
46
+ # it { is_expected.not_to have_permission_for(:user).to_update }
47
+ # it { is_expected.not_to have_permission_for(:user).to_delete }
48
+ #
49
+ # The above method is not related to declarative_authorization privileges,
50
+ # and serve simply as convinience method, below is a table of RESTful actions
51
+ # for each method mentioned above:
52
+ #
53
+ # Method RESTful action
54
+ # ------------------------------------
55
+ # to_read [:index, :show]
56
+ # to_edit [:edit, :update]
57
+ # to_create [:new, :create]
58
+ # to_delete [:destroy]
59
+ #
60
+ # Matcher for RESTful methods is slightly different than that of a single
61
+ # method, following is how RESTful methods request results evaluated:
62
+ #
63
+ # all_requests (of #to_read) matches? does_not_match?
64
+ # -------------------------------------------------------------------
65
+ # {index: true, show: true} true false
66
+ # {index: true, show: false} false false
67
+ # {index: false, show: false} false true
68
+ #
69
+ # @param role [Symbol] role name to matched against
4
70
  def have_permission_for(role)
5
71
  HavePermissionFor.new(role)
6
72
  end
7
73
 
8
- # @private
9
- class HavePermissionFor
74
+ class HavePermissionFor # :nodoc: all
10
75
  include Adapters
11
76
 
12
- attr_reader :controller, :role, :action
77
+ attr_reader :controller, :role, :behave, :actions, :results
13
78
 
14
79
  def initialize(role)
15
- @role = role
16
- @action = :index
80
+ @role = role
17
81
  end
18
82
 
19
83
  def to(action)
20
- @action = action
84
+ @behave = action
85
+ @actions = [behave]
86
+
87
+ self
88
+ end
89
+
90
+ def to_read
91
+ @behave = :read
92
+ @actions = %i(index show)
93
+
94
+ self
95
+ end
96
+
97
+ def to_create
98
+ @behave = :create
99
+ @actions = %i(new create)
100
+
21
101
  self
22
102
  end
23
103
 
104
+ def to_update
105
+ @behave = :update
106
+ @actions = %i(edit update)
107
+
108
+ self
109
+ end
110
+
111
+ def to_delete
112
+ @behave = :delete
113
+ @actions = %i(destroy)
114
+
115
+ self
116
+ end
117
+
118
+ def to_manage
119
+ @behave = :manage
120
+ @actions = %i(index show new create edit update destroy)
121
+
122
+ self
123
+ end
124
+
125
+ def all_requests
126
+ actions.map do |action|
127
+ request = Request.new(controller.class, action, role)
128
+ [action, request.response.status != 403]
129
+ end
130
+ end
131
+
24
132
  def matches?(controller)
25
133
  @controller = controller
134
+ @results = Hash[all_requests]
135
+
136
+ true unless results.value? false
137
+ end
138
+
139
+ def does_not_match?(controller)
140
+ @controller = controller
141
+ @results = Hash[all_requests]
26
142
 
27
- request = Request.new(controller.class, action, role)
28
- request.response.status != 403
143
+ true unless results.value? true
29
144
  end
30
145
 
31
146
  def failure_message
32
- "Expected #{controller.class} to have permission for #{role} to #{action}"
147
+ "Expected #{controller.class} to have permission for #{role} to #{behave}. #{results}"
33
148
  end
34
149
 
35
150
  def failure_message_when_negated
36
- "Did not expect #{controller.class} to have permission for #{role} to #{action}"
151
+ "Did not expect #{controller.class} to have permission for #{role} to #{behave}. #{results}"
37
152
  end
38
153
 
39
154
  def description
40
- "have permission for #{role} on #{action}"
155
+ "have permission for #{role} to #{behave}"
41
156
  end
42
157
  end
43
158
  end
@@ -1,7 +1,7 @@
1
1
  require 'rspec/authorization/matchers/have_permission_for'
2
2
 
3
3
  module RSpec::Authorization
4
- module Matchers
4
+ module Matchers # :nodoc:
5
5
  end
6
6
  end
7
7
 
@@ -1,5 +1,5 @@
1
- module Rspec
1
+ module RSpec
2
2
  module Authorization
3
- VERSION = "0.0.1"
3
+ VERSION = "0.0.2"
4
4
  end
5
5
  end
@@ -3,7 +3,7 @@ require "rspec/authorization/adapters"
3
3
  require "rspec/authorization/matchers"
4
4
  require "rspec/authorization/version"
5
5
 
6
- module RSpec
7
- module Authorization
6
+ module RSpec # :nodoc:
7
+ module Authorization # :nodoc:
8
8
  end
9
9
  end
@@ -4,7 +4,7 @@ require 'rspec/authorization/version'
4
4
 
5
5
  Gem::Specification.new do |spec|
6
6
  spec.name = "rspec-authorization"
7
- spec.version = Rspec::Authorization::VERSION
7
+ spec.version = RSpec::Authorization::VERSION
8
8
  spec.authors = ["Hendra Uzia"]
9
9
  spec.email = ["hendra.uzia@gmail.com"]
10
10
  spec.summary = %q{RSpec matcher for declarative_authorization.}
@@ -18,7 +18,7 @@ Gem::Specification.new do |spec|
18
18
  spec.require_paths = ["lib"]
19
19
 
20
20
  spec.add_runtime_dependency "declarative_authorization"
21
- spec.add_runtime_dependency "rspec-rails"
21
+ spec.add_runtime_dependency "rspec-rails", "~> 3.0"
22
22
 
23
23
  spec.add_development_dependency "bundler", "~> 1.7"
24
24
  spec.add_development_dependency "rails", "~> 4.0"
@@ -0,0 +1,21 @@
1
+ require 'rails_helper'
2
+
3
+ describe ArticlesController do
4
+ it { is_expected.to have_permission_for(:user).to(:index) }
5
+ it { is_expected.not_to have_permission_for(:user).to(:show) }
6
+ it { is_expected.not_to have_permission_for(:user).to_create }
7
+ it { is_expected.not_to have_permission_for(:user).to_update }
8
+ it { is_expected.not_to have_permission_for(:user).to_delete }
9
+
10
+ it { is_expected.to have_permission_for(:premium).to_read }
11
+ it { is_expected.not_to have_permission_for(:premium).to_create }
12
+ it { is_expected.not_to have_permission_for(:premium).to_update }
13
+ it { is_expected.not_to have_permission_for(:premium).to_delete }
14
+
15
+ it { is_expected.to have_permission_for(:writer).to_read }
16
+ it { is_expected.to have_permission_for(:writer).to_create }
17
+ it { is_expected.not_to have_permission_for(:writer).to_update }
18
+ it { is_expected.not_to have_permission_for(:writer).to_delete }
19
+
20
+ it { is_expected.to have_permission_for(:editor).to_manage }
21
+ end
@@ -0,0 +1,28 @@
1
+ require 'rails_helper'
2
+
3
+ include RSpec::Authorization::Adapters
4
+
5
+ describe ExampleGroup do
6
+ let(:klass) { ArticlesController }
7
+ let(:example_group) { ExampleGroup.new(klass) }
8
+
9
+ let(:before_instruction) { ->{ :before } }
10
+ let(:after_instruction) { ->{ :after } }
11
+
12
+ describe "target" do
13
+ subject { example_group.target }
14
+ its(:described_class) { is_expected.to eq klass }
15
+ end
16
+
17
+ describe "#before" do
18
+ specify { expect(example_group.before(&before_instruction).last.block).to eq before_instruction }
19
+ end
20
+
21
+ describe "#after" do
22
+ specify { expect(example_group.after(&after_instruction).first.block).to eq after_instruction }
23
+ end
24
+
25
+ describe "#run_example" do
26
+ specify { expect(example_group.run_example).to be_a_kind_of(Example) }
27
+ end
28
+ end
@@ -4,23 +4,13 @@ include RSpec::Authorization::Adapters
4
4
 
5
5
  describe Example do
6
6
  let(:klass) { GroupTestClass }
7
- let(:group) { Group.new(klass) }
8
- let(:example) { Example.new(group) }
7
+ let(:group) { ExampleGroup.new(klass) }
8
+ let(:example) { Example.new(group.target) }
9
9
 
10
- subject { example }
11
-
12
- its(:group) { is_expected.to eq group }
13
- its(:example) { is_expected.to be_a_kind_of RSpec::Core::Example }
10
+ before { allow_any_instance_of(Example).to receive(:run_before_example) }
14
11
 
15
- context "private" do
16
- describe "#set_example_group_instance" do
17
- before { example.send :set_example_group_instance }
18
- specify { expect(example.example.instance_variable_get :@example_group_instance).to be_a_kind_of group }
19
- end
12
+ subject { example }
20
13
 
21
- describe "#run_before_example" do
22
- before { expect(example.example).to receive(:run_before_example).and_return(true) }
23
- specify { expect(example.example.send :run_before_example).to be_present }
24
- end
25
- end
14
+ its(:group_target) { is_expected.to eq group.target }
15
+ its(:target) { is_expected.to be_a_kind_of RSpec::Core::Example }
26
16
  end
@@ -3,9 +3,9 @@ require 'rails_helper'
3
3
  include RSpec::Authorization::Adapters
4
4
 
5
5
  describe Request do
6
- let(:klass) { PostsController }
7
- let(:action) { :index }
8
- let(:role) { :user }
6
+ let(:klass) { ArticlesController }
7
+ let(:action) { :update }
8
+ let(:role) { :editor }
9
9
  let(:request) { Request.new(klass, action, role) }
10
10
 
11
11
  subject { request }
data/spec/spec_helper.rb CHANGED
@@ -1,3 +1,6 @@
1
+ require "codeclimate-test-reporter"
2
+ CodeClimate::TestReporter.start
3
+
1
4
  require 'rspec/its'
2
5
 
3
6
  $LOAD_PATH << File.expand_path('../../tools', __FILE__)
@@ -3,7 +3,8 @@ require 'rails_test_app'
3
3
 
4
4
  describe RailsTestApp do
5
5
  let(:test_app) { RailsTestApp.new(:version) }
6
- let(:dummy_file) { 'tmp/dummy-rails-test-app' }
6
+ let(:temp_path) { "tmp" }
7
+ let(:dummy_file) { "#{temp_path}/dummy-rails-test-app" }
7
8
 
8
9
  describe ".new" do
9
10
  subject(:test_app) { RailsTestApp.new("4.1.6", "--skip-javascript") }
@@ -18,6 +19,7 @@ describe RailsTestApp do
18
19
  before { allow(test_app).to receive(:create_command).and_return("touch #{dummy_file}") }
19
20
  before { allow(test_app).to receive(:template_param) }
20
21
  before { allow(test_app).to receive(:option) }
22
+ before { `mkdir #{temp_path}` }
21
23
  after { `rm -rf #{dummy_file}` }
22
24
 
23
25
  context "path does not exists" do
@@ -5,8 +5,8 @@ rules = "config/authorization_rules.rb"
5
5
  run "mkdir ../../../config"
6
6
  run "ln -s ../spec/.rails/rails-#{Rails::VERSION::STRING}/#{rules} ../../../#{rules}"
7
7
 
8
- generate "scaffold post --skip-assets --skip-helper"
9
- generate "migration AddUserIdToPosts user:references"
8
+ generate "scaffold article --skip-assets --skip-helper"
9
+ generate "migration AddUserIdToArticles user:references"
10
10
 
11
11
  rake "db:migrate"
12
12
  run "bundle exec rake db:migrate RAILS_ENV=test"
@@ -14,27 +14,38 @@ run "bundle exec rake db:migrate RAILS_ENV=test"
14
14
  first_line = /\A.*/
15
15
  last_line = /^.*\Z/
16
16
 
17
- inject_into_file "app/models/post.rb", %q{
17
+ inject_into_file "app/models/article.rb", %q{
18
18
  belongs_to :user
19
19
  }, after: first_line
20
20
 
21
21
  inject_into_file "app/models/user.rb", %q{
22
- has_many :posts
22
+ has_many :articles
23
23
  }, after: first_line
24
24
 
25
25
  inject_into_file "config/authorization_rules.rb", %q{
26
+ role :editor do
27
+ has_permission_on :articles, to: :manage
28
+ end
29
+
30
+ role :writer do
31
+ has_permission_on :articles, to: %i(read create)
32
+ end
33
+
34
+ role :premium do
35
+ has_permission_on :articles, to: :read
36
+ end
37
+
26
38
  role :user do
27
- has_permission_on :posts, to: :manage
39
+ has_permission_on :articles, to: :index
28
40
  end
29
41
  }, after: first_line
30
42
 
31
43
  inject_into_file "app/controllers/application_controller.rb", %q{
32
44
  def current_user
33
- User.where(id: session[:user_id]).presence
34
45
  end
35
46
  }, before: last_line
36
47
 
37
- inject_into_file "app/controllers/posts_controller.rb", %q{
48
+ inject_into_file "app/controllers/articles_controller.rb", %q{
38
49
  filter_resource_access
39
50
  }, after: first_line
40
51
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec-authorization
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hendra Uzia
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-11-05 00:00:00.000000000 Z
11
+ date: 2014-11-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: declarative_authorization
@@ -28,16 +28,16 @@ dependencies:
28
28
  name: rspec-rails
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ">="
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '0'
33
+ version: '3.0'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ">="
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '0'
40
+ version: '3.0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: bundler
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -120,15 +120,18 @@ files:
120
120
  - ".gitignore"
121
121
  - ".rspec"
122
122
  - ".ruby-version"
123
+ - ".travis.yml"
124
+ - ".yardopts"
123
125
  - Gemfile
124
126
  - Guardfile
127
+ - HISTORY.md
125
128
  - LICENSE.txt
126
129
  - README.md
127
130
  - Rakefile
128
131
  - lib/rspec/authorization.rb
129
132
  - lib/rspec/authorization/adapters.rb
130
133
  - lib/rspec/authorization/adapters/example.rb
131
- - lib/rspec/authorization/adapters/group.rb
134
+ - lib/rspec/authorization/adapters/example_group.rb
132
135
  - lib/rspec/authorization/adapters/request.rb
133
136
  - lib/rspec/authorization/adapters/route.rb
134
137
  - lib/rspec/authorization/matchers.rb
@@ -136,9 +139,9 @@ files:
136
139
  - lib/rspec/authorization/version.rb
137
140
  - rspec-authorization.gemspec
138
141
  - spec/.keep
139
- - spec/controllers/posts_controller_spec.rb
142
+ - spec/controllers/articles_controller_spec.rb
143
+ - spec/lib/rspec/authorization/adapters/example_group_spec.rb
140
144
  - spec/lib/rspec/authorization/adapters/example_spec.rb
141
- - spec/lib/rspec/authorization/adapters/group_spec.rb
142
145
  - spec/lib/rspec/authorization/adapters/request_spec.rb
143
146
  - spec/lib/rspec/authorization/adapters/route_spec.rb
144
147
  - spec/rails_helper.rb
@@ -176,12 +179,13 @@ specification_version: 4
176
179
  summary: RSpec matcher for declarative_authorization.
177
180
  test_files:
178
181
  - spec/.keep
179
- - spec/controllers/posts_controller_spec.rb
182
+ - spec/controllers/articles_controller_spec.rb
183
+ - spec/lib/rspec/authorization/adapters/example_group_spec.rb
180
184
  - spec/lib/rspec/authorization/adapters/example_spec.rb
181
- - spec/lib/rspec/authorization/adapters/group_spec.rb
182
185
  - spec/lib/rspec/authorization/adapters/request_spec.rb
183
186
  - spec/lib/rspec/authorization/adapters/route_spec.rb
184
187
  - spec/rails_helper.rb
185
188
  - spec/spec_helper.rb
186
189
  - spec/support/group_test_class.rb
187
190
  - spec/tools/rails_test_app_spec.rb
191
+ has_rdoc:
@@ -1,14 +0,0 @@
1
- module RSpec::Authorization
2
- module Adapters
3
- class Group
4
- class << self
5
- def new(klass)
6
- RSpec::Core::ExampleGroup.describe klass do
7
- include RSpec::Rails::ControllerExampleGroup
8
- end
9
- end
10
- end
11
- end
12
- end
13
- end
14
-
@@ -1,11 +0,0 @@
1
- require 'rails_helper'
2
-
3
- describe PostsController do
4
- it { is_expected.to have_permission_for(:user).to(:index) }
5
- it { is_expected.to have_permission_for(:user).to(:new) }
6
- it { is_expected.to have_permission_for(:user).to(:create) }
7
- it { is_expected.to have_permission_for(:user).to(:show) }
8
- it { is_expected.to have_permission_for(:user).to(:edit) }
9
- it { is_expected.to have_permission_for(:user).to(:update) }
10
- it { is_expected.to have_permission_for(:user).to(:destroy) }
11
- end
@@ -1,13 +0,0 @@
1
- require 'rails_helper'
2
-
3
- include RSpec::Authorization::Adapters
4
-
5
- describe Group do
6
- let(:klass) { GroupTestClass }
7
- let(:group) { Group.new(klass) }
8
-
9
- describe ".new" do
10
- subject { group }
11
- its(:described_class) { is_expected.to eq GroupTestClass }
12
- end
13
- end