cancan 1.1.1 → 1.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/CHANGELOG.rdoc +15 -0
- data/lib/cancan.rb +1 -0
- data/lib/cancan/ability.rb +95 -135
- data/lib/cancan/active_record_additions.rb +10 -9
- data/lib/cancan/can_definition.rb +114 -0
- data/lib/cancan/controller_additions.rb +61 -51
- data/lib/cancan/controller_resource.rb +16 -9
- data/lib/cancan/exceptions.rb +11 -11
- data/lib/cancan/resource_authorization.rb +21 -19
- data/spec/cancan/ability_spec.rb +40 -28
- data/spec/cancan/active_record_additions_spec.rb +8 -8
- data/spec/cancan/can_definition_spec.rb +44 -0
- data/spec/cancan/controller_additions_spec.rb +9 -9
- data/spec/cancan/controller_resource_spec.rb +9 -9
- data/spec/cancan/exceptions_spec.rb +5 -5
- data/spec/cancan/resource_authorization_spec.rb +35 -17
- data/spec/spec_helper.rb +1 -1
- metadata +6 -4
@@ -1,35 +1,35 @@
|
|
1
1
|
module CanCan
|
2
|
-
|
2
|
+
|
3
3
|
# This module is automatically included into all controllers.
|
4
4
|
# It also makes the "can?" and "cannot?" methods available to all views.
|
5
5
|
module ControllerAdditions
|
6
6
|
module ClassMethods
|
7
7
|
# Sets up a before filter which loads and authorizes the current resource. This performs both
|
8
8
|
# load_resource and authorize_resource and accepts the same arguments. See those methods for details.
|
9
|
-
#
|
9
|
+
#
|
10
10
|
# class BooksController < ApplicationController
|
11
11
|
# load_and_authorize_resource
|
12
12
|
# end
|
13
|
-
#
|
13
|
+
#
|
14
14
|
def load_and_authorize_resource(options = {})
|
15
15
|
ResourceAuthorization.add_before_filter(self, :load_and_authorize_resource, options)
|
16
16
|
end
|
17
|
-
|
17
|
+
|
18
18
|
# Sets up a before filter which loads the appropriate model resource into an instance variable.
|
19
19
|
# For example, given an ArticlesController it will load the current article into the @article
|
20
20
|
# instance variable. It does this by either calling Article.find(params[:id]) or
|
21
21
|
# Article.new(params[:article]) depending upon the action. It does nothing for the "index"
|
22
22
|
# action.
|
23
|
-
#
|
23
|
+
#
|
24
24
|
# Call this method directly on the controller class.
|
25
|
-
#
|
25
|
+
#
|
26
26
|
# class BooksController < ApplicationController
|
27
27
|
# load_resource
|
28
28
|
# end
|
29
|
-
#
|
29
|
+
#
|
30
30
|
# A resource is not loaded if the instance variable is already set. This makes it easy to override
|
31
31
|
# the behavior through a before_filter on certain actions.
|
32
|
-
#
|
32
|
+
#
|
33
33
|
# class BooksController < ApplicationController
|
34
34
|
# before_filter :find_book_by_permalink, :only => :show
|
35
35
|
# load_resource
|
@@ -40,107 +40,117 @@ module CanCan
|
|
40
40
|
# @book = Book.find_by_permalink!(params[:id)
|
41
41
|
# end
|
42
42
|
# end
|
43
|
-
#
|
43
|
+
#
|
44
44
|
# See load_and_authorize_resource to automatically authorize the resource too.
|
45
|
-
#
|
45
|
+
#
|
46
46
|
# Options:
|
47
47
|
# [:+only+]
|
48
48
|
# Only applies before filter to given actions.
|
49
|
-
#
|
49
|
+
#
|
50
50
|
# [:+except+]
|
51
51
|
# Does not apply before filter to given actions.
|
52
|
-
#
|
52
|
+
#
|
53
53
|
# [:+nested+]
|
54
54
|
# Specify which resource this is nested under.
|
55
|
-
#
|
55
|
+
#
|
56
56
|
# load_resource :nested => :author
|
57
|
-
#
|
57
|
+
#
|
58
58
|
# Deep nesting can be defined in an array.
|
59
|
-
#
|
59
|
+
#
|
60
60
|
# load_resource :nested => [:publisher, :author]
|
61
|
-
#
|
61
|
+
#
|
62
|
+
# [:+name+]
|
63
|
+
# The name of the resource if it cannot be determined from controller (string or symbol).
|
64
|
+
#
|
65
|
+
# load_resource :name => :article
|
66
|
+
#
|
62
67
|
# [:+resource+]
|
63
68
|
# The class to use for the model (string or constant).
|
64
|
-
#
|
69
|
+
#
|
65
70
|
# [:+collection+]
|
66
71
|
# Specify which actions are resource collection actions in addition to :+index+. This
|
67
72
|
# is usually not necessary because it will try to guess depending on if an :+id+
|
68
73
|
# is present in +params+.
|
69
|
-
#
|
74
|
+
#
|
70
75
|
# load_resource :collection => [:sort, :list]
|
71
|
-
#
|
76
|
+
#
|
72
77
|
# [:+new+]
|
73
78
|
# Specify which actions are new resource actions in addition to :+new+ and :+create+.
|
74
79
|
# Pass an action name into here if you would like to build a new resource instead of
|
75
80
|
# fetch one.
|
76
|
-
#
|
81
|
+
#
|
77
82
|
# load_resource :new => :build
|
78
|
-
#
|
83
|
+
#
|
79
84
|
def load_resource(options = {})
|
80
85
|
ResourceAuthorization.add_before_filter(self, :load_resource, options)
|
81
86
|
end
|
82
|
-
|
87
|
+
|
83
88
|
# Sets up a before filter which authorizes the current resource using the instance variable.
|
84
89
|
# For example, if you have an ArticlesController it will check the @article instance variable
|
85
90
|
# and ensure the user can perform the current action on it. Under the hood it is doing
|
86
91
|
# something like the following.
|
87
|
-
#
|
92
|
+
#
|
88
93
|
# authorize!(params[:action].to_sym, @article || Article)
|
89
|
-
#
|
94
|
+
#
|
90
95
|
# Call this method directly on the controller class.
|
91
|
-
#
|
96
|
+
#
|
92
97
|
# class BooksController < ApplicationController
|
93
98
|
# authorize_resource
|
94
99
|
# end
|
95
|
-
#
|
100
|
+
#
|
96
101
|
# See load_and_authorize_resource to automatically load the resource too.
|
97
|
-
#
|
102
|
+
#
|
98
103
|
# Options:
|
99
104
|
# [:+only+]
|
100
105
|
# Only applies before filter to given actions.
|
101
|
-
#
|
106
|
+
#
|
102
107
|
# [:+except+]
|
103
108
|
# Does not apply before filter to given actions.
|
104
|
-
#
|
109
|
+
#
|
110
|
+
# [:+name+]
|
111
|
+
# The name of the resource if it cannot be determined from controller (string or symbol).
|
112
|
+
#
|
113
|
+
# load_resource :name => :article
|
114
|
+
#
|
105
115
|
# [:+resource+]
|
106
116
|
# The class to use for the model (string or constant). Alternatively pass a symbol
|
107
117
|
# to represent a resource which does not have a class.
|
108
|
-
#
|
118
|
+
#
|
109
119
|
def authorize_resource(options = {})
|
110
120
|
ResourceAuthorization.add_before_filter(self, :authorize_resource, options)
|
111
121
|
end
|
112
122
|
end
|
113
|
-
|
123
|
+
|
114
124
|
def self.included(base)
|
115
125
|
base.extend ClassMethods
|
116
126
|
base.helper_method :can?, :cannot?
|
117
127
|
end
|
118
|
-
|
128
|
+
|
119
129
|
# Raises a CanCan::AccessDenied exception if the current_ability cannot
|
120
130
|
# perform the given action. This is usually called in a controller action or
|
121
131
|
# before filter to perform the authorization.
|
122
|
-
#
|
132
|
+
#
|
123
133
|
# def show
|
124
134
|
# @article = Article.find(params[:id])
|
125
135
|
# authorize! :read, @article
|
126
136
|
# end
|
127
|
-
#
|
137
|
+
#
|
128
138
|
# A :message option can be passed to specify a different message.
|
129
|
-
#
|
139
|
+
#
|
130
140
|
# authorize! :read, @article, :message => "Not authorized to read #{@article.name}"
|
131
|
-
#
|
141
|
+
#
|
132
142
|
# You can rescue from the exception in the controller to customize how unauthorized
|
133
143
|
# access is displayed to the user.
|
134
|
-
#
|
144
|
+
#
|
135
145
|
# class ApplicationController < ActionController::Base
|
136
146
|
# rescue_from CanCan::AccessDenied do |exception|
|
137
147
|
# flash[:error] = exception.message
|
138
148
|
# redirect_to root_url
|
139
149
|
# end
|
140
150
|
# end
|
141
|
-
#
|
151
|
+
#
|
142
152
|
# See the CanCan::AccessDenied exception for more details on working with the exception.
|
143
|
-
#
|
153
|
+
#
|
144
154
|
# See the load_and_authorize_resource method to automatically add the authorize! behavior
|
145
155
|
# to the default RESTful actions.
|
146
156
|
def authorize!(action, subject, *args)
|
@@ -150,46 +160,46 @@ module CanCan
|
|
150
160
|
end
|
151
161
|
raise AccessDenied.new(message, action, subject) if cannot?(action, subject, *args)
|
152
162
|
end
|
153
|
-
|
163
|
+
|
154
164
|
def unauthorized!(message = nil)
|
155
165
|
raise ImplementationRemoved, "The unauthorized! method has been removed from CanCan, use authorize! instead."
|
156
166
|
end
|
157
|
-
|
167
|
+
|
158
168
|
# Creates and returns the current user's ability and caches it. If you
|
159
169
|
# want to override how the Ability is defined then this is the place.
|
160
170
|
# Just define the method in the controller to change behavior.
|
161
|
-
#
|
171
|
+
#
|
162
172
|
# def current_ability
|
163
173
|
# # instead of Ability.new(current_user)
|
164
174
|
# @current_ability ||= UserAbility.new(current_account)
|
165
175
|
# end
|
166
|
-
#
|
176
|
+
#
|
167
177
|
# Notice it is important to cache the ability object so it is not
|
168
178
|
# recreated every time.
|
169
179
|
def current_ability
|
170
180
|
@current_ability ||= ::Ability.new(current_user)
|
171
181
|
end
|
172
|
-
|
182
|
+
|
173
183
|
# Use in the controller or view to check the user's permission for a given action
|
174
184
|
# and object.
|
175
|
-
#
|
185
|
+
#
|
176
186
|
# can? :destroy, @project
|
177
|
-
#
|
187
|
+
#
|
178
188
|
# You can also pass the class instead of an instance (if you don't have one handy).
|
179
|
-
#
|
189
|
+
#
|
180
190
|
# <% if can? :create, Project %>
|
181
191
|
# <%= link_to "New Project", new_project_path %>
|
182
192
|
# <% end %>
|
183
|
-
#
|
193
|
+
#
|
184
194
|
# This simply calls "can?" on the current_ability. See Ability#can?.
|
185
195
|
def can?(*args)
|
186
196
|
current_ability.can?(*args)
|
187
197
|
end
|
188
|
-
|
198
|
+
|
189
199
|
# Convenience method which works the same as "can?" but returns the opposite value.
|
190
|
-
#
|
200
|
+
#
|
191
201
|
# cannot? :destroy, @project
|
192
|
-
#
|
202
|
+
#
|
193
203
|
def cannot?(*args)
|
194
204
|
current_ability.cannot?(*args)
|
195
205
|
end
|
@@ -1,4 +1,7 @@
|
|
1
1
|
module CanCan
|
2
|
+
# Used internally to load and authorize a given controller resource.
|
3
|
+
# This manages finding or building an instance of the resource. If a
|
4
|
+
# parent is given it will go through the association.
|
2
5
|
class ControllerResource # :nodoc:
|
3
6
|
def initialize(controller, name, parent = nil, options = {})
|
4
7
|
raise ImplementationRemoved, "The :class option has been renamed to :resource for specifying the class in CanCan." if options.has_key? :class
|
@@ -8,13 +11,17 @@ module CanCan
|
|
8
11
|
@options = options
|
9
12
|
end
|
10
13
|
|
14
|
+
# Returns the class used for this resource. This can be overriden by the :resource option.
|
15
|
+
# Sometimes one will use a symbol as the resource if a class does not exist for it. In that
|
16
|
+
# case "find" and "build" should not be called on it.
|
11
17
|
def model_class
|
12
|
-
|
18
|
+
resource_class = @options[:resource]
|
19
|
+
if resource_class.nil?
|
13
20
|
@name.to_s.camelize.constantize
|
14
|
-
elsif
|
15
|
-
|
21
|
+
elsif resource_class.kind_of? String
|
22
|
+
resource_class.constantize
|
16
23
|
else
|
17
|
-
|
24
|
+
resource_class # could be a symbol
|
18
25
|
end
|
19
26
|
end
|
20
27
|
|
@@ -22,12 +29,10 @@ module CanCan
|
|
22
29
|
self.model_instance ||= base.find(id)
|
23
30
|
end
|
24
31
|
|
32
|
+
# Build a new instance of this resource. If it is a class we just call "new" otherwise
|
33
|
+
# it's an associaiton and "build" is used.
|
25
34
|
def build(attributes)
|
26
|
-
|
27
|
-
self.model_instance ||= base.new(attributes)
|
28
|
-
else
|
29
|
-
self.model_instance ||= base.build(attributes)
|
30
|
-
end
|
35
|
+
self.model_instance ||= (base.kind_of?(Class) ? base.new(attributes) : base.build(attributes))
|
31
36
|
end
|
32
37
|
|
33
38
|
def model_instance
|
@@ -40,6 +45,8 @@ module CanCan
|
|
40
45
|
|
41
46
|
private
|
42
47
|
|
48
|
+
# The object that methods (such as "find", "new" or "build") are called on.
|
49
|
+
# If there is a parent it will be the association, otherwise it will be the model's class.
|
43
50
|
def base
|
44
51
|
@parent ? @parent.model_instance.send(@name.to_s.pluralize) : model_class
|
45
52
|
end
|
data/lib/cancan/exceptions.rb
CHANGED
@@ -1,41 +1,41 @@
|
|
1
1
|
module CanCan
|
2
2
|
# A general CanCan exception
|
3
3
|
class Error < StandardError; end
|
4
|
-
|
4
|
+
|
5
5
|
# Raised when removed code is called, an alternative solution is provided in message.
|
6
6
|
class ImplementationRemoved < Error; end
|
7
|
-
|
7
|
+
|
8
8
|
# This error is raised when a user isn't allowed to access a given controller action.
|
9
9
|
# This usually happens within a call to ControllerAdditions#authorize! but can be
|
10
10
|
# raised manually.
|
11
|
-
#
|
11
|
+
#
|
12
12
|
# raise CanCan::AccessDenied.new("Not authorized!", :read, Article)
|
13
|
-
#
|
13
|
+
#
|
14
14
|
# The passed message, action, and subject are optional and can later be retrieved when
|
15
15
|
# rescuing from the exception.
|
16
|
-
#
|
16
|
+
#
|
17
17
|
# exception.message # => "Not authorized!"
|
18
18
|
# exception.action # => :read
|
19
19
|
# exception.subject # => Article
|
20
|
-
#
|
21
|
-
# If the message is not specified (or is nil) it will default to "You are
|
20
|
+
#
|
21
|
+
# If the message is not specified (or is nil) it will default to "You are not authorized
|
22
22
|
# to access this page." This default can be overridden by setting default_message.
|
23
|
-
#
|
23
|
+
#
|
24
24
|
# exception.default_message = "Default error message"
|
25
25
|
# exception.message # => "Default error message"
|
26
|
-
#
|
26
|
+
#
|
27
27
|
# See ControllerAdditions#authorized! for more information on rescuing from this exception.
|
28
28
|
class AccessDenied < Error
|
29
29
|
attr_reader :action, :subject
|
30
30
|
attr_writer :default_message
|
31
|
-
|
31
|
+
|
32
32
|
def initialize(message = nil, action = nil, subject = nil)
|
33
33
|
@message = message
|
34
34
|
@action = action
|
35
35
|
@subject = subject
|
36
36
|
@default_message = "You are not authorized to access this page."
|
37
37
|
end
|
38
|
-
|
38
|
+
|
39
39
|
def to_s
|
40
40
|
@message || @default_message
|
41
41
|
end
|
@@ -1,44 +1,46 @@
|
|
1
1
|
module CanCan
|
2
|
+
# Handle the load and authorization controller logic so we don't clutter up all controllers with non-interface methods.
|
3
|
+
# This class is used internally, so you do not need to call methods directly on it.
|
2
4
|
class ResourceAuthorization # :nodoc:
|
3
|
-
attr_reader :params
|
4
|
-
|
5
5
|
def self.add_before_filter(controller_class, method, options = {})
|
6
6
|
controller_class.before_filter(options.slice(:only, :except)) do |controller|
|
7
7
|
ResourceAuthorization.new(controller, controller.params, options.except(:only, :except)).send(method)
|
8
8
|
end
|
9
9
|
end
|
10
|
-
|
10
|
+
|
11
11
|
def initialize(controller, params, options = {})
|
12
12
|
@controller = controller
|
13
13
|
@params = params
|
14
14
|
@options = options
|
15
15
|
end
|
16
|
-
|
16
|
+
|
17
17
|
def load_and_authorize_resource
|
18
18
|
load_resource
|
19
19
|
authorize_resource
|
20
20
|
end
|
21
|
-
|
21
|
+
|
22
22
|
def load_resource
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
resource.
|
23
|
+
if collection_actions.include? @params[:action].to_sym
|
24
|
+
parent_resource
|
25
|
+
else
|
26
|
+
if new_actions.include? @params[:action].to_sym
|
27
|
+
resource.build(@params[model_name.to_sym])
|
28
|
+
elsif @params[:id]
|
29
|
+
resource.find(@params[:id])
|
28
30
|
end
|
29
31
|
end
|
30
32
|
end
|
31
|
-
|
33
|
+
|
32
34
|
def authorize_resource
|
33
|
-
@controller.authorize!(params[:action].to_sym, resource.model_instance || resource.model_class)
|
35
|
+
@controller.authorize!(@params[:action].to_sym, resource.model_instance || resource.model_class)
|
34
36
|
end
|
35
|
-
|
37
|
+
|
36
38
|
private
|
37
|
-
|
39
|
+
|
38
40
|
def resource
|
39
41
|
@resource ||= ControllerResource.new(@controller, model_name, parent_resource, @options)
|
40
42
|
end
|
41
|
-
|
43
|
+
|
42
44
|
def parent_resource
|
43
45
|
parent = nil
|
44
46
|
[@options[:nested]].flatten.compact.each do |name|
|
@@ -52,15 +54,15 @@ module CanCan
|
|
52
54
|
end
|
53
55
|
parent
|
54
56
|
end
|
55
|
-
|
57
|
+
|
56
58
|
def model_name
|
57
|
-
params[:controller].sub("Controller", "").underscore.split('/').last.singularize
|
59
|
+
@options[:name] || @params[:controller].sub("Controller", "").underscore.split('/').last.singularize
|
58
60
|
end
|
59
|
-
|
61
|
+
|
60
62
|
def collection_actions
|
61
63
|
[:index] + [@options[:collection]].flatten
|
62
64
|
end
|
63
|
-
|
65
|
+
|
64
66
|
def new_actions
|
65
67
|
[:new, :create] + [@options[:new]].flatten
|
66
68
|
end
|
data/spec/cancan/ability_spec.rb
CHANGED
@@ -5,17 +5,17 @@ describe CanCan::Ability do
|
|
5
5
|
@ability = Object.new
|
6
6
|
@ability.extend(CanCan::Ability)
|
7
7
|
end
|
8
|
-
|
8
|
+
|
9
9
|
it "should be able to :read anything" do
|
10
10
|
@ability.can :read, :all
|
11
11
|
@ability.can?(:read, String).should be_true
|
12
12
|
@ability.can?(:read, 123).should be_true
|
13
13
|
end
|
14
|
-
|
14
|
+
|
15
15
|
it "should not have permission to do something it doesn't know about" do
|
16
16
|
@ability.can?(:foodfight, String).should be_false
|
17
17
|
end
|
18
|
-
|
18
|
+
|
19
19
|
it "should return what block returns on a can call" do
|
20
20
|
@ability.can :read, :all
|
21
21
|
@ability.can :read, Symbol do |sym|
|
@@ -24,21 +24,21 @@ describe CanCan::Ability do
|
|
24
24
|
@ability.can?(:read, Symbol).should be_nil
|
25
25
|
@ability.can?(:read, :some_symbol).should == :some_symbol
|
26
26
|
end
|
27
|
-
|
27
|
+
|
28
28
|
it "should pass class with object if :all objects are accepted" do
|
29
29
|
@ability.can :preview, :all do |object_class, object|
|
30
30
|
[object_class, object]
|
31
31
|
end
|
32
32
|
@ability.can?(:preview, 123).should == [Fixnum, 123]
|
33
33
|
end
|
34
|
-
|
34
|
+
|
35
35
|
it "should pass class with no object if :all objects are accepted and class is passed directly" do
|
36
36
|
@ability.can :preview, :all do |object_class, object|
|
37
37
|
[object_class, object]
|
38
38
|
end
|
39
39
|
@ability.can?(:preview, Hash).should == [Hash, nil]
|
40
40
|
end
|
41
|
-
|
41
|
+
|
42
42
|
it "should pass action and object for global manage actions" do
|
43
43
|
@ability.can :manage, Array do |action, object|
|
44
44
|
[action, object]
|
@@ -46,14 +46,14 @@ describe CanCan::Ability do
|
|
46
46
|
@ability.can?(:stuff, [1, 2]).should == [:stuff, [1, 2]]
|
47
47
|
@ability.can?(:stuff, Array).should == [:stuff, nil]
|
48
48
|
end
|
49
|
-
|
49
|
+
|
50
50
|
it "should alias update or destroy actions to modify action" do
|
51
51
|
@ability.alias_action :update, :destroy, :to => :modify
|
52
52
|
@ability.can(:modify, :all) { :modify_called }
|
53
53
|
@ability.can?(:update, 123).should == :modify_called
|
54
54
|
@ability.can?(:destroy, 123).should == :modify_called
|
55
55
|
end
|
56
|
-
|
56
|
+
|
57
57
|
it "should return block result for action, object_class, and object for any action" do
|
58
58
|
@ability.can :manage, :all do |action, object_class, object|
|
59
59
|
[action, object_class, object]
|
@@ -61,56 +61,56 @@ describe CanCan::Ability do
|
|
61
61
|
@ability.can?(:foo, 123).should == [:foo, Fixnum, 123]
|
62
62
|
@ability.can?(:bar, Fixnum).should == [:bar, Fixnum, nil]
|
63
63
|
end
|
64
|
-
|
64
|
+
|
65
65
|
it "should automatically alias index and show into read calls" do
|
66
66
|
@ability.can :read, :all
|
67
67
|
@ability.can?(:index, 123).should be_true
|
68
68
|
@ability.can?(:show, 123).should be_true
|
69
69
|
end
|
70
|
-
|
70
|
+
|
71
71
|
it "should automatically alias new and edit into create and update respectively" do
|
72
72
|
@ability.can(:create, :all) { :create_called }
|
73
73
|
@ability.can(:update, :all) { :update_called }
|
74
74
|
@ability.can?(:new, 123).should == :create_called
|
75
75
|
@ability.can?(:edit, 123).should == :update_called
|
76
76
|
end
|
77
|
-
|
77
|
+
|
78
78
|
it "should not respond to prepare (now using initialize)" do
|
79
79
|
@ability.should_not respond_to(:prepare)
|
80
80
|
end
|
81
|
-
|
81
|
+
|
82
82
|
it "should offer cannot? method which is simply invert of can?" do
|
83
83
|
@ability.cannot?(:tie, String).should be_true
|
84
84
|
end
|
85
|
-
|
85
|
+
|
86
86
|
it "should be able to specify multiple actions and match any" do
|
87
87
|
@ability.can [:read, :update], :all
|
88
88
|
@ability.can?(:read, 123).should be_true
|
89
89
|
@ability.can?(:update, 123).should be_true
|
90
90
|
@ability.can?(:count, 123).should be_false
|
91
91
|
end
|
92
|
-
|
92
|
+
|
93
93
|
it "should be able to specify multiple classes and match any" do
|
94
94
|
@ability.can :update, [String, Array]
|
95
95
|
@ability.can?(:update, "foo").should be_true
|
96
96
|
@ability.can?(:update, []).should be_true
|
97
97
|
@ability.can?(:update, 123).should be_false
|
98
98
|
end
|
99
|
-
|
99
|
+
|
100
100
|
it "should support custom objects in the can definition" do
|
101
101
|
@ability.can :read, :stats
|
102
102
|
@ability.can?(:read, :stats).should be_true
|
103
103
|
@ability.can?(:update, :stats).should be_false
|
104
104
|
@ability.can?(:read, :nonstats).should be_false
|
105
105
|
end
|
106
|
-
|
106
|
+
|
107
107
|
it "should support 'cannot' method to define what user cannot do" do
|
108
108
|
@ability.can :read, :all
|
109
109
|
@ability.cannot :read, Integer
|
110
110
|
@ability.can?(:read, "foo").should be_true
|
111
111
|
@ability.can?(:read, 123).should be_false
|
112
112
|
end
|
113
|
-
|
113
|
+
|
114
114
|
it "should support block on 'cannot' method" do
|
115
115
|
@ability.can :read, :all
|
116
116
|
@ability.cannot :read, Integer do |int|
|
@@ -120,19 +120,19 @@ describe CanCan::Ability do
|
|
120
120
|
@ability.can?(:read, 3).should be_true
|
121
121
|
@ability.can?(:read, 123).should be_false
|
122
122
|
end
|
123
|
-
|
123
|
+
|
124
124
|
it "should append aliased actions" do
|
125
125
|
@ability.alias_action :update, :to => :modify
|
126
126
|
@ability.alias_action :destroy, :to => :modify
|
127
127
|
@ability.aliased_actions[:modify].should == [:update, :destroy]
|
128
128
|
end
|
129
|
-
|
129
|
+
|
130
130
|
it "should clear aliased actions" do
|
131
131
|
@ability.alias_action :update, :to => :modify
|
132
132
|
@ability.clear_aliased_actions
|
133
133
|
@ability.aliased_actions[:modify].should be_nil
|
134
134
|
end
|
135
|
-
|
135
|
+
|
136
136
|
it "should pass additional arguments to block from can?" do
|
137
137
|
@ability.can :read, Integer do |int, x|
|
138
138
|
int > x
|
@@ -140,52 +140,64 @@ describe CanCan::Ability do
|
|
140
140
|
@ability.can?(:read, 2, 1).should be_true
|
141
141
|
@ability.can?(:read, 2, 3).should be_false
|
142
142
|
end
|
143
|
-
|
143
|
+
|
144
144
|
it "should use conditions as third parameter and determine abilities from it" do
|
145
145
|
@ability.can :read, Array, :first => 1, :last => 3
|
146
146
|
@ability.can?(:read, [1, 2, 3]).should be_true
|
147
147
|
@ability.can?(:read, [1, 2, 3, 4]).should be_false
|
148
148
|
@ability.can?(:read, Array).should be_true
|
149
149
|
end
|
150
|
-
|
150
|
+
|
151
151
|
it "should allow an array of options in conditions hash" do
|
152
152
|
@ability.can :read, Array, :first => [1, 3, 5]
|
153
153
|
@ability.can?(:read, [1, 2, 3]).should be_true
|
154
154
|
@ability.can?(:read, [2, 3]).should be_false
|
155
155
|
@ability.can?(:read, [3, 4]).should be_true
|
156
156
|
end
|
157
|
-
|
157
|
+
|
158
158
|
it "should allow a range of options in conditions hash" do
|
159
159
|
@ability.can :read, Array, :first => 1..3
|
160
160
|
@ability.can?(:read, [1, 2, 3]).should be_true
|
161
161
|
@ability.can?(:read, [3, 4]).should be_true
|
162
162
|
@ability.can?(:read, [4, 5]).should be_false
|
163
163
|
end
|
164
|
-
|
164
|
+
|
165
165
|
it "should allow nested hashes in conditions hash" do
|
166
166
|
@ability.can :read, Array, :first => { :length => 5 }
|
167
167
|
@ability.can?(:read, ["foo", "bar"]).should be_false
|
168
168
|
@ability.can?(:read, ["test1", "foo"]).should be_true
|
169
169
|
end
|
170
|
-
|
170
|
+
|
171
|
+
it "should allow nested hash of arrays and match any element" do
|
172
|
+
@ability.can :read, Array, :first => { :to_i => 3 }
|
173
|
+
@ability.can?(:read, [[1, 2, 3]]).should be_true
|
174
|
+
@ability.can?(:read, [[4, 5, 6]]).should be_false
|
175
|
+
end
|
176
|
+
|
171
177
|
it "should return conditions for a given ability" do
|
172
178
|
@ability.can :read, Array, :first => 1, :last => 3
|
173
179
|
@ability.conditions(:show, Array).should == {:first => 1, :last => 3}
|
174
180
|
end
|
175
|
-
|
181
|
+
|
176
182
|
it "should raise an exception when a block is used on condition" do
|
177
183
|
@ability.can :read, Array do |a|
|
178
184
|
true
|
179
185
|
end
|
180
186
|
lambda { @ability.conditions(:show, Array) }.should raise_error(CanCan::Error, "Cannot determine ability conditions from block for :show Array")
|
181
187
|
end
|
182
|
-
|
188
|
+
|
183
189
|
it "should return an empty hash for conditions when there are no conditions" do
|
184
190
|
@ability.can :read, Array
|
185
191
|
@ability.conditions(:show, Array).should == {}
|
186
192
|
end
|
187
|
-
|
193
|
+
|
188
194
|
it "should return false when performed on an action which isn't defined" do
|
189
195
|
@ability.conditions(:foo, Array).should == false
|
190
196
|
end
|
197
|
+
|
198
|
+
it "should has eated cheezburger" do
|
199
|
+
lambda {
|
200
|
+
@ability.can? :has, :cheezburger
|
201
|
+
}.should raise_exception(CanCan::Error, "Nom nom nom. I eated it.")
|
202
|
+
end
|
191
203
|
end
|