cancan 1.0.2 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.rdoc CHANGED
@@ -1,3 +1,26 @@
1
+ 1.1.0 (April 17, 2010)
2
+
3
+ * Supporting arrays, ranges, and nested hashes in ability conditions
4
+
5
+ * Removing "unauthorized!" method in favor of "authorize!" in controllers
6
+
7
+ * Adding action, subject and default_message abilities to AccessDenied exception - see issue #40
8
+
9
+ * Adding caching to current_ability controller method, if you're overriding this be sure to add caching too.
10
+
11
+ * Adding "accessible_by" method to Active Record for fetching records matching a specific ability
12
+
13
+ * Adding conditions behavior to Ability#can and fetch with Ability#conditions - see issue #53
14
+
15
+ * Renaming :class option to :resource for load_and_authorize_resource which now supports a symbol for non models - see issue #45
16
+
17
+ * Properly handle Admin::AbilitiesController in params[:controller] - see issue #46
18
+
19
+ * Adding be_able_to RSpec matcher (thanks dchelimsky), requires Ruby 1.8.7 or higher - see issue #54
20
+
21
+ * Support additional arguments to can? which get passed to the block - see issue #48
22
+
23
+
1
24
  1.0.2 (Dec 30, 2009)
2
25
 
3
26
  * Adding clear_aliased_actions to Ability which removes previously defined actions including defaults - see issue #20
data/README.rdoc CHANGED
@@ -1,29 +1,26 @@
1
1
  = CanCan
2
2
 
3
- RDocs[http://rdoc.info/projects/ryanb/cancan] | Wiki[http://wiki.github.com/ryanb/cancan] | Screencast[http://railscasts.com/episodes/192-authorization-with-cancan] | Metrics[http://getcaliper.com/caliper/project?repo=git%3A%2F%2Fgithub.com%2Fryanb%2Fcancan.git] | Tests[http://runcoderun.com/ryanb/cancan]
3
+ Wiki[http://wiki.github.com/ryanb/cancan] | RDocs[http://rdoc.info/projects/ryanb/cancan] | Screencast[http://railscasts.com/episodes/192-authorization-with-cancan] | Metrics[http://getcaliper.com/caliper/project?repo=git%3A%2F%2Fgithub.com%2Fryanb%2Fcancan.git]
4
4
 
5
- This is a simple authorization solution for Ruby on Rails to restrict what a given user is allowed to access in the application. This is completely decoupled from any role based implementation allowing you to define user roles the way you want. All permissions are stored in a single location for convenience.
5
+ CanCan is an authorization solution for Ruby on Rails. This restricts what a given user is allowed to access throughout the application. It is completely decoupled from any role based implementation and focusses on keeping permission logic in a single location (the +Ability+ class) so it is not duplicated across controllers, views, and database queries.
6
+
7
+ This assumes you already have authentication (such as Authlogic[http://github.com/binarylogic/authlogic] or Devise[http://github.com/plataformatec/devise]). This will provide a +current_user+ method which CanCan relies on. See {Changing Defaults}[http://wiki.github.com/ryanb/cancan/changing-defaults] if you need different behavior.
6
8
 
7
- This assumes you already have authentication (such as Authlogic[http://github.com/binarylogic/authlogic]) which provides a current_user model.
8
9
 
9
10
  == Installation
10
11
 
11
- You can set it up as a gem in your environment.rb file.
12
-
13
- config.gem "cancan"
14
-
15
- And then install the gem.
12
+ CanCan is provided as a gem. Simply include it in your environment.rb or Gemfile.
16
13
 
17
- sudo rake gems:install
14
+ config.gem "cancan"
18
15
 
19
- Alternatively you can install it as a Rails plugin.
16
+ Alternatively it can be installed as a plugin.
20
17
 
21
18
  script/plugin install git://github.com/ryanb/cancan.git
22
19
 
23
20
 
24
21
  == Getting Started
25
22
 
26
- First, define a class called Ability in "models/ability.rb".
23
+ First, define a class called +Ability+ in "models/ability.rb". It should look something like this.
27
24
 
28
25
  class Ability
29
26
  include CanCan::Ability
@@ -39,30 +36,34 @@ First, define a class called Ability in "models/ability.rb".
39
36
 
40
37
  This is where all permissions will go. See the "Defining Abilities" section below for more information.
41
38
 
42
- You can access the current permissions at any point using the "can?" and "cannot?" methods in the view.
39
+ The current user's permissions can be accessed using the "can?" and "cannot?" methods in the view and controller.
43
40
 
44
41
  <% if can? :update, @article %>
45
42
  <%= link_to "Edit", edit_article_path(@article) %>
46
43
  <% end %>
47
44
 
48
- You can also use these methods in a controller along with the "unauthorized!" method to restrict access.
45
+ See {Checking Abilities}[http://wiki.github.com/ryanb/cancan/checking-abilities] for more information
46
+
47
+ The "authorize!" method in the controller will raise an exception if the user is not able to perform the given action.
49
48
 
50
49
  def show
51
50
  @article = Article.find(params[:id])
52
- unauthorized! if cannot? :read, @article
51
+ authorize! :read, @article
53
52
  end
54
53
 
55
- Setting this for every action can be tedious, therefore the load_and_authorize_resource method is also provided to automatically authorize all actions in a RESTful style resource controller. It will set up a before filter which loads the resource into the instance variable and authorizes it.
54
+ Setting this for every action can be tedious, therefore the +load_and_authorize_resource+ method is provided to automatically authorize all actions in a RESTful style resource controller. It will set up a before filter which loads the resource into the instance variable and authorizes it for each action.
56
55
 
57
56
  class ArticlesController < ApplicationController
58
57
  load_and_authorize_resource
59
58
 
60
59
  def show
61
- # @article is already loaded
60
+ # @article is already loaded and authorized
62
61
  end
63
62
  end
64
63
 
65
- If the user authorization fails, a CanCan::AccessDenied exception will be raised. You can catch this and modify its behavior in the ApplicationController.
64
+ See {Authorizing Controller Actions}[http://wiki.github.com/ryanb/cancan/authorizing-controller-actions] for more information
65
+
66
+ If the user authorization fails a CanCan::AccessDenied exception will be raised. You can catch this and modify its behavior in the +ApplicationController+.
66
67
 
67
68
  class ApplicationController < ActionController::Base
68
69
  rescue_from CanCan::AccessDenied do |exception|
@@ -71,132 +72,74 @@ If the user authorization fails, a CanCan::AccessDenied exception will be raised
71
72
  end
72
73
  end
73
74
 
75
+ See {Exception Handling}[http://wiki.github.com/ryanb/cancan/exception-handling] for more information.
76
+
74
77
 
75
78
  == Defining Abilities
76
79
 
77
- As shown above, the Ability class is where all user permissions are defined. The user model is passed into the initialize method so you are free to modify the permissions based on the user's attributes. This way CanCan is completely decoupled with how you choose to handle roles.
80
+ As shown above, the +Ability+ class is where all user permissions are defined. The user model is passed into the initialize method so the permissions can be modified based on any user attributes. CanCan makes no assumptions about how roles are handled in your application. See {Role Based Authorization}[http://wiki.github.com/ryanb/cancan/role-based-authorization] for an example.
78
81
 
79
- The "can" method accepts two arguments, the first one is the action you're setting the permission for, the second one is the class of object you're setting it on.
82
+ The +can+ method is used to define permissions and requires two arguments. The first one is the action you're setting the permission for, the second one is the class of object you're setting it on.
80
83
 
81
84
  can :update, Article
82
85
 
83
- You can pass an array for either of these parameters to match any one.
86
+ You can pass an array for either of these parameters to match any one. In this case the user will have the ability to update or destroy both articles and comments.
84
87
 
85
88
  can [:update, :destroy], [Article, Comment]
86
89
 
87
- In this case the user has the ability to update or destroy both articles and comments.
88
-
89
- You can pass a block to provide logic based on the article's attributes.
90
+ Use :+manage+ to represent any action and :+all+ to represent any class. Here are some examples.
90
91
 
91
- can :update, Article do |article|
92
- article && article.user == user
93
- end
94
-
95
- If the block returns true then the user has that :update ability for that article, otherwise he will be denied access. It's possible for the passed in model to be nil if one isn't specified, so be sure to take that into consideration.
92
+ can :manage, Article # has permissions to do anything to articles
93
+ can :read, :all # has permission to read any model
94
+ can :manage, :all # has permission to do anything to any model
96
95
 
97
- You can pass :all to reference every type of object. In this case the object type will be passed into the block as well (just in case object is nil).
96
+ You can pass a hash of conditions as the third argument to further restrict what the user is able to access. Here the user will only have permission to read active projects which he owns.
98
97
 
99
- can :read, :all do |object_class, object|
100
- object_class != Order
101
- end
98
+ can :read, Project, :active => true, :user_id => user.id
102
99
 
103
- Here the user has permission to read all objects except orders.
100
+ See {Defining Abilities with Hashes}[http://wiki.github.com/ryanb/cancan/defining-abilities-with-hashes] for more information.
104
101
 
105
- You can also pass :manage as the action which will match any action. In this case the action is passed to the block.
102
+ Blocks can also be used if you need more control.
106
103
 
107
- can :manage, Comment do |action, comment|
108
- action != :destroy
104
+ can :update, Project do |project|
105
+ project && project.groups.include?(user.group)
109
106
  end
110
-
111
- Finally, the "cannot" method works similar to "can" but defines which abilities cannot be done.
112
-
113
- can :read, :all
114
- cannot :read, Product
115
-
116
107
 
117
- == Checking Abilities
118
-
119
- Use the "can?" method in the controller or view to check the user's permission for a given action and object.
120
-
121
- can? :destroy, @project
122
-
123
- You can also pass the class instead of an instance (if you don't have one handy).
124
-
125
- <% if can? :create, Project %>
126
- <%= link_to "New Project", new_project_path %>
127
- <% end %>
128
-
129
- The "cannot?" method is for convenience and performs the opposite check of "can?"
130
-
131
- cannot? :destroy, @project
108
+ If the block returns true then the user has that :+update+ ability for that project, otherwise he will be denied access. See {Defining Abilities with Blocks}[http://wiki.github.com/ryanb/cancan/defining-abilities-with-blocks] for more information.
132
109
 
133
110
 
134
111
  == Aliasing Actions
135
112
 
136
- You can use the "alias_action" method to alias one or more actions into one.
137
-
138
- alias_action :update, :destroy, :to => :modify
139
- can :modify, Comment
140
- can? :update, Comment # => true
141
-
142
- The following aliases are added by default for conveniently mapping common controller actions.
113
+ You will usually be working with four actions when defining and checking permissions: :+read+, :+create+, :+update+, :+destroy+. These aren't the same as the 7 RESTful actions in Rails. CanCan adds some default aliases for mapping those actions.
143
114
 
144
115
  alias_action :index, :show, :to => :read
145
116
  alias_action :new, :to => :create
146
117
  alias_action :edit, :to => :update
147
118
 
119
+ Notice the +edit+ action is aliased to +update+. If the user is able to update a record he also has permission to edit it. You can define your own aliases in the +Ability+ class
148
120
 
149
- == Authorizing Controller Actions
150
-
151
- As mentioned in the Getting Started section, you can use the +load_and_authorize_resource+ method in your controller to load the resource into an instance variable and authorize it. If you have a nested resource you can specify that as well.
152
-
153
- load_and_authorize_resource :nested => :author
154
-
155
- You can also pass an array to the :+nested+ attribute for deep nesting.
156
-
157
- If you want to customize the loading behavior on certain actions, you can do so in a before filter.
158
-
159
- class BooksController < ApplicationController
160
- before_filter :find_book_by_permalink, :only => :show
161
- load_and_authorize_resource
162
-
163
- private
164
-
165
- def find_book_by_permalink
166
- @book = Book.find_by_permalink!(params[:id)
167
- end
168
- end
169
-
170
- Here the @book instance variable is already set so it will not be loaded again for that action. This works for nested resources as well.
171
-
172
-
173
- == Assumptions & Configuring
174
-
175
- CanCan makes two assumptions about your application.
121
+ alias_action :update, :destroy, :to => :modify
122
+ can :modify, Comment
123
+ can? :update, Comment # => true
176
124
 
177
- * You have an Ability class which defines the permissions.
178
- * You have a current_user method in the controller which returns the current user model.
125
+ See {Custom Actions}[http://wiki.github.com/ryanb/cancan/custom-actions] for information on adding other actions.
179
126
 
180
- You can override these by overriding the "current_ability" method in your ApplicationController.
181
127
 
182
- def current_ability
183
- UserAbility.new(current_account) # instead of Ability.new(current_user)
184
- end
128
+ == Fetching Records
185
129
 
186
- That's it!
130
+ In the controller +index+ action you may want to fetch only the records which the user has permission to read. You can do this with the +accessible_by+ scope.
187
131
 
132
+ @articles = Article.accessible_by(current_ability)
188
133
 
189
- == Testing Abilities
134
+ See {Fetching Records}[http://wiki.github.com/ryanb/cancan/fetching-records] for more information.
190
135
 
191
- It is very easy to test the Ability model since you can call "can?" directly on it as you would in the view or controller.
192
136
 
193
- def test "user can only destroy projects which he owns"
194
- user = User.new
195
- ability = Ability.new(user)
196
- assert ability.can?(:destroy, Project.new(:user => user))
197
- assert ability.cannot?(:destroy, Project.new)
198
- end
137
+ == Additional Docs
199
138
 
139
+ * {Upgrading to 1.1}[http://wiki.github.com/ryanb/cancan/upgrading-to-11]
140
+ * {Testing Abilities}[http://wiki.github.com/ryanb/cancan/testing-abilities]
141
+ * {Accessing Request Data}[http://wiki.github.com/ryanb/cancan/accessing-request-data]
142
+ * {See more}[http://wiki.github.com/ryanb/cancan/]
200
143
 
201
144
  == Special Thanks
202
145
 
data/lib/cancan.rb CHANGED
@@ -1,10 +1,6 @@
1
- module CanCan
2
- # This error is raised when a user isn't allowed to access a given
3
- # controller action. See ControllerAdditions#unauthorized! for details.
4
- class AccessDenied < StandardError; end
5
- end
6
-
7
- require File.dirname(__FILE__) + '/cancan/ability'
8
- require File.dirname(__FILE__) + '/cancan/controller_resource'
9
- require File.dirname(__FILE__) + '/cancan/resource_authorization'
10
- require File.dirname(__FILE__) + '/cancan/controller_additions'
1
+ require 'cancan/ability'
2
+ require 'cancan/controller_resource'
3
+ require 'cancan/resource_authorization'
4
+ require 'cancan/controller_additions'
5
+ require 'cancan/active_record_additions'
6
+ require 'cancan/exceptions'
@@ -16,8 +16,6 @@ module CanCan
16
16
  # end
17
17
  #
18
18
  module Ability
19
- attr_accessor :user
20
-
21
19
  # Use to check the user's permission for a given action and object.
22
20
  #
23
21
  # can? :destroy, @project
@@ -26,6 +24,15 @@ module CanCan
26
24
  #
27
25
  # can? :create, Project
28
26
  #
27
+ # Any additional arguments will be passed into the "can" block definition. This
28
+ # can be used to pass more information about the user's request for example.
29
+ #
30
+ # can? :create, Project, request.remote_ip
31
+ #
32
+ # can :create Project do |project, remote_ip|
33
+ # # ...
34
+ # end
35
+ #
29
36
  # Not only can you use the can? method in the controller and view (see ControllerAdditions),
30
37
  # but you can also call it directly on an ability instance.
31
38
  #
@@ -40,14 +47,10 @@ module CanCan
40
47
  # assert ability.cannot?(:destroy, Project.new)
41
48
  # end
42
49
  #
43
- def can?(action, noun)
44
- (@can_definitions || []).reverse.each do |base_behavior, defined_action, defined_noun, defined_block|
45
- defined_actions = expand_actions(defined_action)
46
- defined_nouns = [defined_noun].flatten
47
- if includes_action?(defined_actions, action) && includes_noun?(defined_nouns, noun)
48
- result = can_perform_action?(action, noun, defined_actions, defined_nouns, defined_block)
49
- return base_behavior ? result : !result
50
- end
50
+ def can?(action, subject, *extra_args)
51
+ matching_can_definition(action, subject) do |base_behavior, defined_actions, defined_subjects, defined_conditions, defined_block|
52
+ result = can_perform_action?(action, subject, defined_actions, defined_subjects, defined_conditions, defined_block, extra_args)
53
+ return base_behavior ? result : !result
51
54
  end
52
55
  false
53
56
  end
@@ -70,17 +73,27 @@ module CanCan
70
73
  # can [:update, :destroy], [Article, Comment]
71
74
  #
72
75
  # In this case the user has the ability to update or destroy both articles and comments.
76
+ #
77
+ # You can pass a hash of conditions as the third argument.
73
78
  #
74
- # You can pass a block to provide logic based on the article's attributes.
79
+ # can :read, Project, :active => true, :user_id => user.id
80
+ #
81
+ # Here the user can only see active projects which he owns. See ControllerAdditions#conditions for a way to
82
+ # use this in database queries.
83
+ #
84
+ # If the conditions hash does not give you enough control over defining abilities, you can use a block to
85
+ # write any Ruby code you want.
75
86
  #
76
- # can :update, Article do |article|
77
- # article && article.user == user
87
+ # can :update, Project do |project|
88
+ # project && project.groups.include?(user.group)
78
89
  # end
79
90
  #
80
- # If the block returns true then the user has that :update ability for that article, otherwise he
91
+ # If the block returns true then the user has that :update ability for that project, otherwise he
81
92
  # will be denied access. It's possible for the passed in model to be nil if one isn't specified,
82
93
  # so be sure to take that into consideration.
83
94
  #
95
+ # The downside to using a block is that it cannot be used to generate conditions for database queries.
96
+ #
84
97
  # You can pass :all to reference every type of object. In this case the object type will be passed
85
98
  # into the block as well (just in case object is nil).
86
99
  #
@@ -103,9 +116,9 @@ module CanCan
103
116
  # can :read, :stats
104
117
  # can? :read, :stats # => true
105
118
  #
106
- def can(action, noun, &block)
119
+ def can(action, subject, conditions = nil, &block)
107
120
  @can_definitions ||= []
108
- @can_definitions << [true, action, noun, block]
121
+ @can_definitions << [true, action, subject, conditions, block]
109
122
  end
110
123
 
111
124
  # Define an ability which cannot be done. Accepts the same arguments as "can".
@@ -120,9 +133,9 @@ module CanCan
120
133
  # product.invisible?
121
134
  # end
122
135
  #
123
- def cannot(action, noun, &block)
136
+ def cannot(action, subject, conditions = nil, &block)
124
137
  @can_definitions ||= []
125
- @can_definitions << [false, action, noun, block]
138
+ @can_definitions << [false, action, subject, conditions, block]
126
139
  end
127
140
 
128
141
  # Alias one or more actions into another one.
@@ -170,8 +183,37 @@ module CanCan
170
183
  @aliased_actions = {}
171
184
  end
172
185
 
186
+ # Returns a hash of conditions which match the given ability. This is useful if you need to generate a database
187
+ # query based on the current ability.
188
+ #
189
+ # can :read, Article, :visible => true
190
+ # conditions :read, Article # returns { :visible => true }
191
+ #
192
+ # Normally you will not call this method directly, but instead go through ActiveRecordAdditions#accessible_by method.
193
+ #
194
+ # If the ability is not defined then false is returned so be sure to take that into consideration.
195
+ # If the ability is defined using a block then this will raise an exception since a hash of conditions cannot be
196
+ # determined from that.
197
+ def conditions(action, subject)
198
+ matching_can_definition(action, subject) do |base_behavior, defined_actions, defined_subjects, defined_conditions, defined_block|
199
+ raise Error, "Cannot determine ability conditions from block for #{action.inspect} #{subject.inspect}" if defined_block
200
+ return defined_conditions || {}
201
+ end
202
+ false
203
+ end
204
+
173
205
  private
174
206
 
207
+ def matching_can_definition(action, subject, &block)
208
+ (@can_definitions || []).reverse.each do |base_behavior, defined_action, defined_subject, defined_conditions, defined_block|
209
+ defined_actions = expand_actions(defined_action)
210
+ defined_subjects = [defined_subject].flatten
211
+ if includes_action?(defined_actions, action) && includes_subject?(defined_subjects, subject)
212
+ return block.call(base_behavior, defined_actions, defined_subjects, defined_conditions, defined_block)
213
+ end
214
+ end
215
+ end
216
+
175
217
  def default_alias_actions
176
218
  {
177
219
  :read => [:index, :show],
@@ -190,15 +232,35 @@ module CanCan
190
232
  end.flatten
191
233
  end
192
234
 
193
- def can_perform_action?(action, noun, defined_actions, defined_nouns, defined_block)
194
- if defined_block.nil?
195
- true
196
- else
235
+ def can_perform_action?(action, subject, defined_actions, defined_subjects, defined_conditions, defined_block, extra_args)
236
+ if defined_block
197
237
  block_args = []
198
238
  block_args << action if defined_actions.include?(:manage)
199
- block_args << (noun.class == Class ? noun : noun.class) if defined_nouns.include?(:all)
200
- block_args << (noun.class == Class ? nil : noun)
201
- return defined_block.call(*block_args)
239
+ block_args << (subject.class == Class ? subject : subject.class) if defined_subjects.include?(:all)
240
+ block_args << (subject.class == Class ? nil : subject)
241
+ block_args += extra_args
242
+ defined_block.call(*block_args)
243
+ elsif defined_conditions
244
+ if subject.class == Class
245
+ true
246
+ else
247
+ matches_conditions? subject, defined_conditions
248
+ end
249
+ else
250
+ true
251
+ end
252
+ end
253
+
254
+ def matches_conditions?(subject, defined_conditions)
255
+ defined_conditions.all? do |name, value|
256
+ attribute = subject.send(name)
257
+ if value.kind_of?(Hash)
258
+ matches_conditions? attribute, value
259
+ elsif value.kind_of?(Array) || value.kind_of?(Range)
260
+ value.include? attribute
261
+ else
262
+ attribute == value
263
+ end
202
264
  end
203
265
  end
204
266
 
@@ -206,8 +268,8 @@ module CanCan
206
268
  actions.include?(:manage) || actions.include?(action)
207
269
  end
208
270
 
209
- def includes_noun?(nouns, noun)
210
- nouns.include?(:all) || nouns.include?(noun) || nouns.any? { |c| c.kind_of?(Class) && noun.kind_of?(c) }
271
+ def includes_subject?(subjects, subject)
272
+ subjects.include?(:all) || subjects.include?(subject) || subjects.any? { |c| c.kind_of?(Class) && subject.kind_of?(c) }
211
273
  end
212
274
  end
213
275
  end
@@ -0,0 +1,42 @@
1
+ module CanCan
2
+ # This module is automatically included into all Active Record.
3
+ module ActiveRecordAdditions
4
+ module ClassMethods
5
+ # Returns a scope which fetches only the records that the passed ability
6
+ # can perform a given action on. The action defaults to :read. This
7
+ # is usually called from a controller and passed the +current_ability+.
8
+ #
9
+ # @articles = Article.accessible_by(current_ability)
10
+ #
11
+ # Here only the articles which the user is able to read will be returned.
12
+ # If the user does not have permission to read any articles then an empty
13
+ # result is returned. Since this is a scope it can be combined with any
14
+ # other scopes or pagination.
15
+ #
16
+ # An alternative action can optionally be passed as a second argument.
17
+ #
18
+ # @articles = Article.accessible_by(current_ability, :update)
19
+ #
20
+ # Here only the articles which the user can update are returned. This
21
+ # internally uses Ability#conditions method, see that for more information.
22
+ def accessible_by(ability, action = :read)
23
+ conditions = ability.conditions(action, self) || {:id => nil}
24
+ if respond_to? :where
25
+ where(conditions)
26
+ else
27
+ scoped(:conditions => conditions)
28
+ end
29
+ end
30
+ end
31
+
32
+ def self.included(base)
33
+ base.extend ClassMethods
34
+ end
35
+ end
36
+ end
37
+
38
+ if defined? ActiveRecord
39
+ ActiveRecord::Base.class_eval do
40
+ include CanCan::ActiveRecordAdditions
41
+ end
42
+ end
@@ -12,7 +12,7 @@ module CanCan
12
12
  # end
13
13
  #
14
14
  def load_and_authorize_resource(options = {})
15
- before_filter(options.slice(:only, :except)) { |c| ResourceAuthorization.new(c, c.params, options.except(:only, :except)).load_and_authorize_resource }
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.
@@ -59,8 +59,8 @@ module CanCan
59
59
  #
60
60
  # load_resource :nested => [:publisher, :author]
61
61
  #
62
- # [:+class+]
63
- # The class to use for the model.
62
+ # [:+resource+]
63
+ # The class to use for the model (string or constant).
64
64
  #
65
65
  # [:+collection+]
66
66
  # Specify which actions are resource collection actions in addition to :+index+. This
@@ -77,7 +77,7 @@ module CanCan
77
77
  # load_resource :new => :build
78
78
  #
79
79
  def load_resource(options = {})
80
- before_filter(options.slice(:only, :except)) { |c| ResourceAuthorization.new(c, c.params, options.except(:only, :except)).load_resource }
80
+ ResourceAuthorization.add_before_filter(self, :load_resource, options)
81
81
  end
82
82
 
83
83
  # Sets up a before filter which authorizes the current resource using the instance variable.
@@ -85,7 +85,7 @@ module CanCan
85
85
  # and ensure the user can perform the current action on it. Under the hood it is doing
86
86
  # something like the following.
87
87
  #
88
- # unauthorized! if cannot?(params[:action].to_sym, @article || Article)
88
+ # authorize!(params[:action].to_sym, @article || Article)
89
89
  #
90
90
  # Call this method directly on the controller class.
91
91
  #
@@ -102,11 +102,12 @@ module CanCan
102
102
  # [:+except+]
103
103
  # Does not apply before filter to given actions.
104
104
  #
105
- # [:+class+]
106
- # The class to use for the model.
105
+ # [:+resource+]
106
+ # The class to use for the model (string or constant). Alternatively pass a symbol
107
+ # to represent a resource which does not have a class.
107
108
  #
108
109
  def authorize_resource(options = {})
109
- before_filter(options.slice(:only, :except)) { |c| ResourceAuthorization.new(c, c.params, options.except(:only, :except)).authorize_resource }
110
+ ResourceAuthorization.add_before_filter(self, :authorize_resource, options)
110
111
  end
111
112
  end
112
113
 
@@ -115,18 +116,21 @@ module CanCan
115
116
  base.helper_method :can?, :cannot?
116
117
  end
117
118
 
118
- # Raises the CanCan::AccessDenied exception. This is often used in a
119
- # controller action to mark a request as unauthorized.
119
+ # Raises a CanCan::AccessDenied exception if the current_ability cannot
120
+ # perform the given action. This is usually called in a controller action or
121
+ # before filter to perform the authorization.
120
122
  #
121
123
  # def show
122
124
  # @article = Article.find(params[:id])
123
- # unauthorized! if cannot? :read, @article
125
+ # authorize! :read, @article
124
126
  # end
125
127
  #
126
- # The unauthorized! method accepts an optional argument which sets the
127
- # message of the exception.
128
+ # A :message option can be passed to specify a different message.
128
129
  #
129
- # You can rescue from the exception in the controller to define the behavior.
130
+ # authorize! :read, @article, :message => "Not authorized to read #{@article.name}"
131
+ #
132
+ # You can rescue from the exception in the controller to customize how unauthorized
133
+ # access is displayed to the user.
130
134
  #
131
135
  # class ApplicationController < ActionController::Base
132
136
  # rescue_from CanCan::AccessDenied do |exception|
@@ -135,22 +139,35 @@ module CanCan
135
139
  # end
136
140
  # end
137
141
  #
138
- # See the load_and_authorize_resource method to automatically add
139
- # the "unauthorized!" behavior to a RESTful controller's actions.
140
- def unauthorized!(message = "You are not authorized to access this page.")
141
- raise AccessDenied, message
142
+ # See the CanCan::AccessDenied exception for more details on working with the exception.
143
+ #
144
+ # See the load_and_authorize_resource method to automatically add the authorize! behavior
145
+ # to the default RESTful actions.
146
+ def authorize!(action, subject, *args)
147
+ message = nil
148
+ if args.last.kind_of?(Hash) && args.last.has_key?(:message)
149
+ message = args.pop[:message]
150
+ end
151
+ raise AccessDenied.new(message, action, subject) if cannot?(action, subject, *args)
152
+ end
153
+
154
+ def unauthorized!(message = nil)
155
+ raise ImplementationRemoved, "The unauthorized! method has been removed from CanCan, use authorize! instead."
142
156
  end
143
157
 
144
- # Creates and returns the current user's ability. You generally do not invoke
145
- # this method directly, instead you can override this method to change its
146
- # behavior if the Ability class or current_user method are different.
158
+ # Creates and returns the current user's ability and caches it. If you
159
+ # want to override how the Ability is defined then this is the place.
160
+ # Just define the method in the controller to change behavior.
147
161
  #
148
162
  # def current_ability
149
- # UserAbility.new(current_account) # instead of Ability.new(current_user)
163
+ # # instead of Ability.new(current_user)
164
+ # @current_ability ||= UserAbility.new(current_account)
150
165
  # end
151
166
  #
167
+ # Notice it is important to cache the ability object so it is not
168
+ # recreated every time.
152
169
  def current_ability
153
- ::Ability.new(current_user)
170
+ @current_ability ||= ::Ability.new(current_user)
154
171
  end
155
172
 
156
173
  # Use in the controller or view to check the user's permission for a given action
@@ -166,7 +183,7 @@ module CanCan
166
183
  #
167
184
  # This simply calls "can?" on the current_ability. See Ability#can?.
168
185
  def can?(*args)
169
- (@current_ability ||= current_ability).can?(*args)
186
+ current_ability.can?(*args)
170
187
  end
171
188
 
172
189
  # Convenience method which works the same as "can?" but returns the opposite value.
@@ -174,7 +191,7 @@ module CanCan
174
191
  # cannot? :destroy, @project
175
192
  #
176
193
  def cannot?(*args)
177
- (@current_ability ||= current_ability).cannot?(*args)
194
+ current_ability.cannot?(*args)
178
195
  end
179
196
  end
180
197
  end
@@ -1,6 +1,7 @@
1
1
  module CanCan
2
2
  class ControllerResource # :nodoc:
3
3
  def initialize(controller, name, parent = nil, options = {})
4
+ raise ImplementationRemoved, "The :class option has been renamed to :resource for specifying the class in CanCan." if options.has_key? :class
4
5
  @controller = controller
5
6
  @name = name
6
7
  @parent = parent
@@ -8,7 +9,13 @@ module CanCan
8
9
  end
9
10
 
10
11
  def model_class
11
- @options[:class] || @name.to_s.camelize.constantize
12
+ if @options[:resource].nil?
13
+ @name.to_s.camelize.constantize
14
+ elsif @options[:resource].kind_of? String
15
+ @options[:resource].constantize
16
+ else
17
+ @options[:resource]
18
+ end
12
19
  end
13
20
 
14
21
  def find(id)
@@ -0,0 +1,43 @@
1
+ module CanCan
2
+ # A general CanCan exception
3
+ class Error < StandardError; end
4
+
5
+ # Raised when removed code is called, an alternative solution is provided in message.
6
+ class ImplementationRemoved < Error; end
7
+
8
+ # This error is raised when a user isn't allowed to access a given controller action.
9
+ # This usually happens within a call to ControllerAdditions#authorize! but can be
10
+ # raised manually.
11
+ #
12
+ # raise CanCan::AccessDenied.new("Not authorized!", :read, Article)
13
+ #
14
+ # The passed message, action, and subject are optional and can later be retrieved when
15
+ # rescuing from the exception.
16
+ #
17
+ # exception.message # => "Not authorized!"
18
+ # exception.action # => :read
19
+ # exception.subject # => Article
20
+ #
21
+ # If the message is not specified (or is nil) it will default to "You are anot authorized
22
+ # to access this page." This default can be overridden by setting default_message.
23
+ #
24
+ # exception.default_message = "Default error message"
25
+ # exception.message # => "Default error message"
26
+ #
27
+ # See ControllerAdditions#authorized! for more information on rescuing from this exception.
28
+ class AccessDenied < Error
29
+ attr_reader :action, :subject
30
+ attr_writer :default_message
31
+
32
+ def initialize(message = nil, action = nil, subject = nil)
33
+ @message = message
34
+ @action = action
35
+ @subject = subject
36
+ @default_message = "You are not authorized to access this page."
37
+ end
38
+
39
+ def to_s
40
+ @message || @default_message
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,13 @@
1
+ Spec::Matchers.define :be_able_to do |*args|
2
+ match do |ability|
3
+ ability.can?(*args)
4
+ end
5
+
6
+ failure_message_for_should do |ability|
7
+ "expected to be able to #{args.map(&:inspect).join(" ")}"
8
+ end
9
+
10
+ failure_message_for_should_not do |ability|
11
+ "expected not to be able to #{args.map(&:inspect).join(" ")}"
12
+ end
13
+ end
@@ -2,6 +2,12 @@ module CanCan
2
2
  class ResourceAuthorization # :nodoc:
3
3
  attr_reader :params
4
4
 
5
+ def self.add_before_filter(controller_class, method, options = {})
6
+ controller_class.before_filter(options.slice(:only, :except)) do |controller|
7
+ new(controller, controller.params, options.except(:only, :except)).send(method)
8
+ end
9
+ end
10
+
5
11
  def initialize(controller, params, options = {})
6
12
  @controller = controller
7
13
  @params = params
@@ -24,7 +30,7 @@ module CanCan
24
30
  end
25
31
 
26
32
  def authorize_resource
27
- @controller.unauthorized! if @controller.cannot?(params[:action].to_sym, resource.model_instance || resource.model_class)
33
+ @controller.authorize!(params[:action].to_sym, resource.model_instance || resource.model_class)
28
34
  end
29
35
 
30
36
  private
@@ -48,7 +54,7 @@ module CanCan
48
54
  end
49
55
 
50
56
  def model_name
51
- params[:controller].split('/').last.singularize
57
+ params[:controller].sub("Controller", "").underscore.split('/').last.singularize
52
58
  end
53
59
 
54
60
  def collection_actions
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/../spec_helper'
1
+ require "spec_helper"
2
2
 
3
3
  describe CanCan::Ability do
4
4
  before(:each) do
@@ -132,4 +132,60 @@ describe CanCan::Ability do
132
132
  @ability.clear_aliased_actions
133
133
  @ability.aliased_actions[:modify].should be_nil
134
134
  end
135
+
136
+ it "should pass additional arguments to block from can?" do
137
+ @ability.can :read, Integer do |int, x|
138
+ int > x
139
+ end
140
+ @ability.can?(:read, 2, 1).should be_true
141
+ @ability.can?(:read, 2, 3).should be_false
142
+ end
143
+
144
+ it "should use conditions as third parameter and determine abilities from it" do
145
+ @ability.can :read, Array, :first => 1, :last => 3
146
+ @ability.can?(:read, [1, 2, 3]).should be_true
147
+ @ability.can?(:read, [1, 2, 3, 4]).should be_false
148
+ @ability.can?(:read, Array).should be_true
149
+ end
150
+
151
+ it "should allow an array of options in conditions hash" do
152
+ @ability.can :read, Array, :first => [1, 3, 5]
153
+ @ability.can?(:read, [1, 2, 3]).should be_true
154
+ @ability.can?(:read, [2, 3]).should be_false
155
+ @ability.can?(:read, [3, 4]).should be_true
156
+ end
157
+
158
+ it "should allow a range of options in conditions hash" do
159
+ @ability.can :read, Array, :first => 1..3
160
+ @ability.can?(:read, [1, 2, 3]).should be_true
161
+ @ability.can?(:read, [3, 4]).should be_true
162
+ @ability.can?(:read, [4, 5]).should be_false
163
+ end
164
+
165
+ it "should allow nested hashes in conditions hash" do
166
+ @ability.can :read, Array, :first => { :length => 5 }
167
+ @ability.can?(:read, ["foo", "bar"]).should be_false
168
+ @ability.can?(:read, ["test1", "foo"]).should be_true
169
+ end
170
+
171
+ it "should return conditions for a given ability" do
172
+ @ability.can :read, Array, :first => 1, :last => 3
173
+ @ability.conditions(:show, Array).should == {:first => 1, :last => 3}
174
+ end
175
+
176
+ it "should raise an exception when a block is used on condition" do
177
+ @ability.can :read, Array do |a|
178
+ true
179
+ end
180
+ lambda { @ability.conditions(:show, Array) }.should raise_error(CanCan::Error, "Cannot determine ability conditions from block for :show Array")
181
+ end
182
+
183
+ it "should return an empty hash for conditions when there are no conditions" do
184
+ @ability.can :read, Array
185
+ @ability.conditions(:show, Array).should == {}
186
+ end
187
+
188
+ it "should return false when performed on an action which isn't defined" do
189
+ @ability.conditions(:foo, Array).should == false
190
+ end
135
191
  end
@@ -0,0 +1,28 @@
1
+ require "spec_helper"
2
+
3
+ describe CanCan::ActiveRecordAdditions do
4
+ before(:each) do
5
+ @model_class = Class.new
6
+ stub(@model_class).scoped { :scoped_stub }
7
+ @model_class.send(:include, CanCan::ActiveRecordAdditions)
8
+ @ability = Object.new
9
+ @ability.extend(CanCan::Ability)
10
+ end
11
+
12
+ it "should call where(:id => nil) when no ability is defined so no records are found" do
13
+ stub(@model_class).where(:id => nil) { :no_where }
14
+ @model_class.accessible_by(@ability, :read).should == :no_where
15
+ end
16
+
17
+ it "should call where with matching ability conditions" do
18
+ @ability.can :read, @model_class, :foo => 1
19
+ stub(@model_class).where(:foo => 1) { :found_records }
20
+ @model_class.accessible_by(@ability, :read).should == :found_records
21
+ end
22
+
23
+ it "should default to :read ability and use scoped when where isn't available" do
24
+ @ability.can :read, @model_class, :foo => 1
25
+ stub(@model_class).scoped(:conditions => {:foo => 1}) { :found_records }
26
+ @model_class.accessible_by(@ability).should == :found_records
27
+ end
28
+ end
@@ -1,33 +1,52 @@
1
- require File.dirname(__FILE__) + '/../spec_helper'
1
+ require "spec_helper"
2
2
 
3
3
  describe CanCan::ControllerAdditions do
4
4
  before(:each) do
5
5
  @controller_class = Class.new
6
6
  @controller = @controller_class.new
7
7
  stub(@controller).params { {} }
8
+ stub(@controller).current_user { :current_user }
8
9
  mock(@controller_class).helper_method(:can?, :cannot?)
9
10
  @controller_class.send(:include, CanCan::ControllerAdditions)
10
11
  end
11
12
 
12
- it "should raise access denied with default message when calling unauthorized!" do
13
- lambda {
14
- @controller.unauthorized!
15
- }.should raise_error(CanCan::AccessDenied, "You are not authorized to access this page.")
13
+ it "should raise ImplementationRemoved when attempting to call 'unauthorized!' on a controller" do
14
+ lambda { @controller.unauthorized! }.should raise_error(CanCan::ImplementationRemoved)
15
+ end
16
+
17
+ it "should raise access denied exception if ability us unauthorized to perform a certain action" do
18
+ begin
19
+ @controller.authorize! :read, :foo, 1, 2, 3, :message => "Access denied!"
20
+ rescue CanCan::AccessDenied => e
21
+ e.message.should == "Access denied!"
22
+ e.action.should == :read
23
+ e.subject.should == :foo
24
+ else
25
+ fail "Expected CanCan::AccessDenied exception to be raised"
26
+ end
16
27
  end
17
28
 
18
- it "should raise access denied with custom message when calling unauthorized!" do
19
- lambda {
20
- @controller.unauthorized! "Access denied!"
21
- }.should raise_error(CanCan::AccessDenied, "Access denied!")
29
+ it "should not raise access denied exception if ability is authorized to perform an action" do
30
+ @controller.current_ability.can :read, :foo
31
+ lambda { @controller.authorize!(:read, :foo) }.should_not raise_error
32
+ end
33
+
34
+ it "should raise access denied exception with default message if not specified" do
35
+ begin
36
+ @controller.authorize! :read, :foo
37
+ rescue CanCan::AccessDenied => e
38
+ e.default_message = "Access denied!"
39
+ e.message.should == "Access denied!"
40
+ else
41
+ fail "Expected CanCan::AccessDenied exception to be raised"
42
+ end
22
43
  end
23
44
 
24
45
  it "should have a current_ability method which generates an ability for the current user" do
25
- stub(@controller).current_user { :current_user }
26
46
  @controller.current_ability.should be_kind_of(Ability)
27
47
  end
28
48
 
29
49
  it "should provide a can? and cannot? methods which go through the current ability" do
30
- stub(@controller).current_user { :current_user }
31
50
  @controller.current_ability.should be_kind_of(Ability)
32
51
  @controller.can?(:foo, :bar).should be_false
33
52
  @controller.cannot?(:foo, :bar).should be_true
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/../spec_helper'
1
+ require "spec_helper"
2
2
 
3
3
  describe CanCan::ControllerResource do
4
4
  before(:each) do
@@ -43,7 +43,17 @@ describe CanCan::ControllerResource do
43
43
 
44
44
  it "should use the model class option if provided" do
45
45
  stub(Person).find(123) { :some_resource }
46
- CanCan::ControllerResource.new(@controller, :ability, nil, :class => Person).find(123)
46
+ CanCan::ControllerResource.new(@controller, :ability, nil, :resource => Person).find(123)
47
47
  @controller.instance_variable_get(:@ability).should == :some_resource
48
48
  end
49
+
50
+ it "should convert string to constant for resource" do
51
+ CanCan::ControllerResource.new(@controller, :ability, nil, :resource => "Person").model_class.should == Person
52
+ end
53
+
54
+ it "should raise an exception when specifying :class option since it is no longer used" do
55
+ lambda {
56
+ CanCan::ControllerResource.new(@controller, :ability, nil, :class => Person)
57
+ }.should raise_error(CanCan::ImplementationRemoved)
58
+ end
49
59
  end
@@ -0,0 +1,35 @@
1
+ require "spec_helper"
2
+
3
+ describe CanCan::AccessDenied do
4
+ describe "with action and subject" do
5
+ before(:each) do
6
+ @exception = CanCan::AccessDenied.new(nil, :some_action, :some_subject)
7
+ end
8
+
9
+ it "should have action and subject accessors" do
10
+ @exception.action.should == :some_action
11
+ @exception.subject.should == :some_subject
12
+ end
13
+
14
+ it "should have a changable default message" do
15
+ @exception.message.should == "You are not authorized to access this page."
16
+ @exception.default_message = "Unauthorized!"
17
+ @exception.message.should == "Unauthorized!"
18
+ end
19
+ end
20
+
21
+ describe "with only a message" do
22
+ before(:each) do
23
+ @exception = CanCan::AccessDenied.new("Access denied!")
24
+ end
25
+
26
+ it "should have nil action and subject" do
27
+ @exception.action.should be_nil
28
+ @exception.subject.should be_nil
29
+ end
30
+
31
+ it "should have passed message" do
32
+ @exception.message.should == "Access denied!"
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,33 @@
1
+ require "spec_helper"
2
+
3
+ describe "be_able_to" do
4
+ it "delegates to can?" do
5
+ object = Object.new
6
+ mock(object).can?(:read, 123) { true }
7
+ object.should be_able_to(:read, 123)
8
+ end
9
+
10
+ it "reports a nice failure message for should" do
11
+ object = Object.new
12
+ mock(object).can?(:read, 123) { false }
13
+ expect do
14
+ object.should be_able_to(:read, 123)
15
+ end.should raise_error('expected to be able to :read 123')
16
+ end
17
+
18
+ it "reports a nice failure message for should not" do
19
+ object = Object.new
20
+ mock(object).can?(:read, 123) { true }
21
+ expect do
22
+ object.should_not be_able_to(:read, 123)
23
+ end.should raise_error('expected not to be able to :read 123')
24
+ end
25
+
26
+ it "delegates additional arguments to can? and reports in failure message" do
27
+ object = Object.new
28
+ mock(object).can?(:read, 123, 456) { false }
29
+ expect do
30
+ object.should be_able_to(:read, 123, 456)
31
+ end.should raise_error('expected to be able to :read 123 456')
32
+ end
33
+ end
@@ -1,9 +1,8 @@
1
- require File.dirname(__FILE__) + '/../spec_helper'
1
+ require "spec_helper"
2
2
 
3
3
  describe CanCan::ResourceAuthorization do
4
4
  before(:each) do
5
5
  @controller = Object.new # simple stub for now
6
- stub(@controller).unauthorized! { raise CanCan::AccessDenied }
7
6
  end
8
7
 
9
8
  it "should load the resource into an instance variable if params[:id] is specified" do
@@ -20,6 +19,13 @@ describe CanCan::ResourceAuthorization do
20
19
  @controller.instance_variable_get(:@ability).should == :some_resource
21
20
  end
22
21
 
22
+ it "should properly load resource for namespaced controller when using '::' for namespace" do
23
+ stub(Ability).find(123) { :some_resource }
24
+ authorization = CanCan::ResourceAuthorization.new(@controller, :controller => "Admin::AbilitiesController", :action => "show", :id => 123)
25
+ authorization.load_resource
26
+ @controller.instance_variable_get(:@ability).should == :some_resource
27
+ end
28
+
23
29
  it "should build a new resource with hash if params[:id] is not specified" do
24
30
  stub(Ability).new(:foo => "bar") { :some_resource }
25
31
  authorization = CanCan::ResourceAuthorization.new(@controller, :controller => "abilities", :action => "create", :ability => {:foo => "bar"})
@@ -42,19 +48,15 @@ describe CanCan::ResourceAuthorization do
42
48
 
43
49
  it "should perform authorization using controller action and loaded model" do
44
50
  @controller.instance_variable_set(:@ability, :some_resource)
45
- stub(@controller).cannot?(:show, :some_resource) { true }
51
+ stub(@controller).authorize!(:show, :some_resource) { raise CanCan::AccessDenied }
46
52
  authorization = CanCan::ResourceAuthorization.new(@controller, :controller => "abilities", :action => "show")
47
- lambda {
48
- authorization.authorize_resource
49
- }.should raise_error(CanCan::AccessDenied)
53
+ lambda { authorization.authorize_resource }.should raise_error(CanCan::AccessDenied)
50
54
  end
51
55
 
52
56
  it "should perform authorization using controller action and non loaded model" do
53
- stub(@controller).cannot?(:show, Ability) { true }
57
+ stub(@controller).authorize!(:show, Ability) { raise CanCan::AccessDenied }
54
58
  authorization = CanCan::ResourceAuthorization.new(@controller, :controller => "abilities", :action => "show")
55
- lambda {
56
- authorization.authorize_resource
57
- }.should raise_error(CanCan::AccessDenied)
59
+ lambda { authorization.authorize_resource }.should raise_error(CanCan::AccessDenied)
58
60
  end
59
61
 
60
62
  it "should call load_resource and authorize_resource for load_and_authorize_resource" do
@@ -108,7 +110,7 @@ describe CanCan::ResourceAuthorization do
108
110
 
109
111
  it "should load the model using a custom class" do
110
112
  stub(Person).find(123) { :some_resource }
111
- authorization = CanCan::ResourceAuthorization.new(@controller, {:controller => "abilities", :action => "show", :id => 123}, {:class => Person})
113
+ authorization = CanCan::ResourceAuthorization.new(@controller, {:controller => "abilities", :action => "show", :id => 123}, {:resource => Person})
112
114
  authorization.load_resource
113
115
  @controller.instance_variable_get(:@ability).should == :some_resource
114
116
  end
data/spec/spec_helper.rb CHANGED
@@ -4,7 +4,8 @@ require 'active_support'
4
4
  require 'active_record'
5
5
  require 'action_controller'
6
6
  require 'action_view'
7
- require File.dirname(__FILE__) + '/../lib/cancan.rb'
7
+ require 'cancan'
8
+ require 'cancan/matchers'
8
9
 
9
10
  Spec::Runner.configure do |config|
10
11
  config.mock_with :rr
metadata CHANGED
@@ -1,7 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cancan
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ prerelease: false
5
+ segments:
6
+ - 1
7
+ - 1
8
+ - 0
9
+ version: 1.1.0
5
10
  platform: ruby
6
11
  authors:
7
12
  - Ryan Bates
@@ -9,7 +14,7 @@ autorequire:
9
14
  bindir: bin
10
15
  cert_chain: []
11
16
 
12
- date: 2009-12-30 00:00:00 -08:00
17
+ date: 2010-04-17 00:00:00 -07:00
13
18
  default_executable:
14
19
  dependencies: []
15
20
 
@@ -19,56 +24,59 @@ executables: []
19
24
 
20
25
  extensions: []
21
26
 
22
- extra_rdoc_files:
23
- - README.rdoc
24
- - CHANGELOG.rdoc
25
- - LICENSE
27
+ extra_rdoc_files: []
28
+
26
29
  files:
27
30
  - lib/cancan/ability.rb
31
+ - lib/cancan/active_record_additions.rb
28
32
  - lib/cancan/controller_additions.rb
29
33
  - lib/cancan/controller_resource.rb
34
+ - lib/cancan/exceptions.rb
35
+ - lib/cancan/matchers.rb
30
36
  - lib/cancan/resource_authorization.rb
31
37
  - lib/cancan.rb
32
38
  - spec/cancan/ability_spec.rb
39
+ - spec/cancan/active_record_additions_spec.rb
33
40
  - spec/cancan/controller_additions_spec.rb
34
41
  - spec/cancan/controller_resource_spec.rb
42
+ - spec/cancan/exceptions_spec.rb
43
+ - spec/cancan/matchers_spec.rb
35
44
  - spec/cancan/resource_authorization_spec.rb
36
45
  - spec/spec_helper.rb
46
+ - CHANGELOG.rdoc
37
47
  - LICENSE
38
- - README.rdoc
39
48
  - Rakefile
40
- - CHANGELOG.rdoc
49
+ - README.rdoc
41
50
  - init.rb
42
51
  has_rdoc: true
43
52
  homepage: http://github.com/ryanb/cancan
44
53
  licenses: []
45
54
 
46
55
  post_install_message:
47
- rdoc_options:
48
- - --line-numbers
49
- - --inline-source
50
- - --title
51
- - CanCan
52
- - --main
53
- - README.rdoc
56
+ rdoc_options: []
57
+
54
58
  require_paths:
55
59
  - lib
56
60
  required_ruby_version: !ruby/object:Gem::Requirement
57
61
  requirements:
58
62
  - - ">="
59
63
  - !ruby/object:Gem::Version
64
+ segments:
65
+ - 0
60
66
  version: "0"
61
- version:
62
67
  required_rubygems_version: !ruby/object:Gem::Requirement
63
68
  requirements:
64
69
  - - ">="
65
70
  - !ruby/object:Gem::Version
66
- version: "1.2"
67
- version:
71
+ segments:
72
+ - 1
73
+ - 3
74
+ - 4
75
+ version: 1.3.4
68
76
  requirements: []
69
77
 
70
- rubyforge_project:
71
- rubygems_version: 1.3.5
78
+ rubyforge_project: cancan
79
+ rubygems_version: 1.3.6
72
80
  signing_key:
73
81
  specification_version: 3
74
82
  summary: Simple authorization solution for Rails.