cancan 1.1.1 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|