cancan 1.4.0 → 1.4.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,16 @@
1
+ 1.4.1 (November 12, 2010)
2
+
3
+ * Renaming skip_authorization to skip_authorization_check - see issue #169
4
+
5
+ * Adding :through_association option to load_resource (thanks hunterae) - see issue #171
6
+
7
+ * The :shallow option now works with the :singleton option (thanks nandalopes) - see issue #187
8
+
9
+ * Play nicely with quick_scopes gem (thanks ramontayag) - see issue #183
10
+
11
+ * Fix odd behavior when "cache_classes = false" (thanks mphalliday) - see issue #174
12
+
13
+
1
14
  1.4.0 (October 5, 2010)
2
15
 
3
16
  * Adding Gemfile; to get specs running just +bundle+ and +rake+ - see issue #163
@@ -21,7 +21,7 @@ module CanCan
21
21
  # internally uses Ability#conditions method, see that for more information.
22
22
  def accessible_by(ability, action = :read)
23
23
  query = ability.query(action, self)
24
- if respond_to? :where
24
+ if respond_to?(:where) && respond_to?(:joins)
25
25
  where(query.conditions).joins(query.joins)
26
26
  else
27
27
  scoped(:conditions => query.conditions, :joins => query.joins)
@@ -97,7 +97,7 @@ module CanCan
97
97
  end
98
98
 
99
99
  def matches_subject_class?(subject)
100
- @subjects.any? { |sub| sub.kind_of?(Module) && (subject.kind_of?(sub) || subject.kind_of?(Module) && subject.ancestors.include?(sub)) }
100
+ @subjects.any? { |sub| sub.kind_of?(Module) && (subject.kind_of?(sub) || subject.class.to_s == sub.to_s || subject.kind_of?(Module) && subject.ancestors.include?(sub)) }
101
101
  end
102
102
 
103
103
  def matches_conditions_hash?(subject, conditions = @conditions)
@@ -71,6 +71,10 @@ module CanCan
71
71
  # [:+through+]
72
72
  # Load this resource through another one. This should match the name of the parent instance variable or method.
73
73
  #
74
+ # [:+through_association+]
75
+ # The name of the association to fetch the child records through the parent resource. This is normally not needed
76
+ # because it defaults to the pluralized resource name.
77
+ #
74
78
  # [:+shallow+]
75
79
  # Pass +true+ to allow this resource to be loaded directly when parent is +nil+. Defaults to +false+.
76
80
  #
@@ -172,11 +176,11 @@ module CanCan
172
176
  #
173
177
  # Any arguments are passed to the +after_filter+ it triggers.
174
178
  #
175
- # See skip_authorization to bypass this check on specific controller actions.
179
+ # See skip_authorization_check to bypass this check on specific controller actions.
176
180
  def check_authorization(*args)
177
181
  self.after_filter(*args) do |controller|
178
182
  unless controller.instance_variable_defined?(:@_authorized)
179
- raise AuthorizationNotPerformed, "This action failed the check_authorization because it does not authorize_resource. Add skip_authorization to bypass this check."
183
+ raise AuthorizationNotPerformed, "This action failed the check_authorization because it does not authorize_resource. Add skip_authorization_check to bypass this check."
180
184
  end
181
185
  end
182
186
  end
@@ -184,16 +188,20 @@ module CanCan
184
188
  # Call this in the class of a controller to skip the check_authorization behavior on the actions.
185
189
  #
186
190
  # class HomeController < ApplicationController
187
- # skip_authorization :only => :index
191
+ # skip_authorization_check :only => :index
188
192
  # end
189
193
  #
190
194
  # Any arguments are passed to the +before_filter+ it triggers.
191
- def skip_authorization(*args)
195
+ def skip_authorization_check(*args)
192
196
  self.before_filter(*args) do |controller|
193
197
  controller.instance_variable_set(:@_authorized, true)
194
198
  end
195
199
  end
196
200
 
201
+ def skip_authorization(*args)
202
+ raise ImplementationRemoved, "The CanCan skip_authorization method has been renamed to skip_authorization_check. Please update your code."
203
+ end
204
+
197
205
  def cancan_resource_class
198
206
  if ancestors.map(&:to_s).include? "InheritedResources::Actions"
199
207
  InheritedResource
@@ -61,7 +61,11 @@ module CanCan
61
61
  end
62
62
 
63
63
  def build_resource
64
- resource = resource_base.send(@options[:singleton] ? "build_#{name}" : "new")
64
+ if @options[:singleton] && resource_base.respond_to?("build_#{name}")
65
+ resource = resource_base.send("build_#{name}")
66
+ else
67
+ resource = resource_base.send("new")
68
+ end
65
69
  initial_attributes.each do |name, value|
66
70
  resource.send("#{name}=", value)
67
71
  end
@@ -74,7 +78,7 @@ module CanCan
74
78
  end
75
79
 
76
80
  def find_resource
77
- if @options[:singleton]
81
+ if @options[:singleton] && resource_base.respond_to?(name)
78
82
  resource_base.send(name)
79
83
  else
80
84
  @options[:find_by] ? resource_base.send("find_by_#{@options[:find_by]}!", id_param) : resource_base.find(id_param)
@@ -132,7 +136,7 @@ module CanCan
132
136
  def resource_base
133
137
  if @options[:through]
134
138
  if parent_resource
135
- @options[:singleton] ? parent_resource : parent_resource.send(name.to_s.pluralize)
139
+ @options[:singleton] ? parent_resource : parent_resource.send(@options[:through_association] || name.to_s.pluralize)
136
140
  elsif @options[:shallow]
137
141
  resource_class
138
142
  else
@@ -10,12 +10,14 @@ describe CanCan::ActiveRecordAdditions do
10
10
  end
11
11
 
12
12
  it "should call where('true=false') when no ability is defined so no records are found" do
13
+ stub(@model_class).joins { true } # just so it responds to .joins as well
13
14
  stub(@model_class).where('true=false').stub!.joins(nil) { :no_match }
14
15
  @model_class.accessible_by(@ability, :read).should == :no_match
15
16
  end
16
17
 
17
18
  it "should call where with matching ability conditions" do
18
19
  @ability.can :read, @model_class, :foo => {:bar => 1}
20
+ stub(@model_class).joins { true } # just so it responds to .joins as well
19
21
  stub(@model_class).where(:foos => { :bar => 1 }).stub!.joins([:foo]) { :found_records }
20
22
  @model_class.accessible_by(@ability, :read).should == :found_records
21
23
  end
@@ -54,9 +54,9 @@ describe CanCan::ControllerAdditions do
54
54
  @controller_class.load_resource :foo => :bar, :only => [:show, :index]
55
55
  end
56
56
 
57
- it "skip_authorization should set up a before filter which sets @_authorized to true" do
57
+ it "skip_authorization_check should set up a before filter which sets @_authorized to true" do
58
58
  mock(@controller_class).before_filter(:filter_options) { |options, block| block.call(@controller) }
59
- @controller_class.skip_authorization(:filter_options)
59
+ @controller_class.skip_authorization_check(:filter_options)
60
60
  @controller.instance_variable_get(:@_authorized).should be_true
61
61
  end
62
62
 
@@ -10,11 +10,11 @@ describe CanCan::ControllerResource do
10
10
  end
11
11
 
12
12
  it "should load the resource into an instance variable if params[:id] is specified" do
13
- @params.merge!(:action => "show", :id => 123)
14
- stub(Project).find(123) { :some_project }
13
+ project = Project.create!
14
+ @params.merge!(:action => "show", :id => project.id)
15
15
  resource = CanCan::ControllerResource.new(@controller)
16
16
  resource.load_resource
17
- @controller.instance_variable_get(:@project).should == :some_project
17
+ @controller.instance_variable_get(:@project).should == project
18
18
  end
19
19
 
20
20
  it "should not load resource into an instance variable if already set" do
@@ -26,19 +26,19 @@ describe CanCan::ControllerResource do
26
26
  end
27
27
 
28
28
  it "should properly load resource for namespaced controller" do
29
- @params.merge!(:controller => "admin/projects", :action => "show", :id => 123)
30
- stub(Project).find(123) { :some_project }
29
+ project = Project.create!
30
+ @params.merge!(:controller => "admin/projects", :action => "show", :id => project.id)
31
31
  resource = CanCan::ControllerResource.new(@controller)
32
32
  resource.load_resource
33
- @controller.instance_variable_get(:@project).should == :some_project
33
+ @controller.instance_variable_get(:@project).should == project
34
34
  end
35
35
 
36
36
  it "should properly load resource for namespaced controller when using '::' for namespace" do
37
- @params.merge!(:controller => "Admin::ProjectsController", :action => "show", :id => 123)
38
- stub(Project).find(123) { :some_project }
37
+ project = Project.create!
38
+ @params.merge!(:controller => "Admin::ProjectsController", :action => "show", :id => project.id)
39
39
  resource = CanCan::ControllerResource.new(@controller)
40
40
  resource.load_resource
41
- @controller.instance_variable_get(:@project).should == :some_project
41
+ @controller.instance_variable_get(:@project).should == project
42
42
  end
43
43
 
44
44
  it "should build a new resource with hash if params[:id] is not specified" do
@@ -157,11 +157,11 @@ describe CanCan::ControllerResource do
157
157
  end
158
158
 
159
159
  it "should load parent resource through proper id parameter" do
160
- @params.merge!(:action => "index", :project_id => 123)
161
- stub(Project).find(123) { :some_project }
160
+ project = Project.create!
161
+ @params.merge!(:action => "index", :project_id => project.id)
162
162
  resource = CanCan::ControllerResource.new(@controller, :project, :parent => true)
163
163
  resource.load_resource
164
- @controller.instance_variable_get(:@project).should == :some_project
164
+ @controller.instance_variable_get(:@project).should == project
165
165
  end
166
166
 
167
167
  it "should load resource through the association of another parent resource using instance variable" do
@@ -174,6 +174,16 @@ describe CanCan::ControllerResource do
174
174
  @controller.instance_variable_get(:@project).should == :some_project
175
175
  end
176
176
 
177
+ it "should load resource through the custom association name" do
178
+ @params.merge!(:action => "show", :id => 123)
179
+ category = Object.new
180
+ @controller.instance_variable_set(:@category, category)
181
+ stub(category).custom_projects.stub!.find(123) { :some_project }
182
+ resource = CanCan::ControllerResource.new(@controller, :through => :category, :through_association => :custom_projects)
183
+ resource.load_resource
184
+ @controller.instance_variable_get(:@project).should == :some_project
185
+ end
186
+
177
187
  it "should load resource through the association of another parent resource using method" do
178
188
  @params.merge!(:action => "show", :id => 123)
179
189
  category = Object.new
@@ -185,16 +195,16 @@ describe CanCan::ControllerResource do
185
195
  end
186
196
 
187
197
  it "should not load through parent resource if instance isn't loaded when shallow" do
188
- @params.merge!(:action => "show", :id => 123)
189
- stub(Project).find(123) { :some_project }
198
+ project = Project.create!
199
+ @params.merge!(:action => "show", :id => project.id)
190
200
  resource = CanCan::ControllerResource.new(@controller, :through => :category, :shallow => true)
191
201
  resource.load_resource
192
- @controller.instance_variable_get(:@project).should == :some_project
202
+ @controller.instance_variable_get(:@project).should == project
193
203
  end
194
204
 
195
205
  it "should raise AccessDenied when attempting to load resource through nil" do
196
- @params.merge!(:action => "show", :id => 123)
197
- stub(Project).find(123) { :some_project }
206
+ project = Project.create!
207
+ @params.merge!(:action => "show", :id => project.id)
198
208
  resource = CanCan::ControllerResource.new(@controller, :through => :category)
199
209
  lambda {
200
210
  resource.load_resource
@@ -241,20 +251,35 @@ describe CanCan::ControllerResource do
241
251
  @controller.instance_variable_get(:@project).name.should == "foobar"
242
252
  end
243
253
 
254
+ it "should find record through has_one association with :singleton and :shallow options" do
255
+ project = Project.create!
256
+ @params.merge!(:action => "show", :id => project.id)
257
+ resource = CanCan::ControllerResource.new(@controller, :through => :category, :singleton => true, :shallow => true)
258
+ resource.load_resource
259
+ @controller.instance_variable_get(:@project).should == project
260
+ end
261
+
262
+ it "should build record through has_one association with :singleton and :shallow options" do
263
+ @params.merge!(:action => "create", :project => {:name => "foobar"})
264
+ resource = CanCan::ControllerResource.new(@controller, :through => :category, :singleton => true, :shallow => true)
265
+ resource.load_resource
266
+ @controller.instance_variable_get(:@project).name.should == "foobar"
267
+ end
268
+
244
269
  it "should only authorize :read action on parent resource" do
245
- @params.merge!(:action => "new", :project_id => 123)
246
- stub(Project).find(123) { :some_project }
247
- stub(@controller).authorize!(:read, :some_project) { raise CanCan::AccessDenied }
270
+ project = Project.create!
271
+ @params.merge!(:action => "new", :project_id => project.id)
272
+ stub(@controller).authorize!(:read, project) { raise CanCan::AccessDenied }
248
273
  resource = CanCan::ControllerResource.new(@controller, :project, :parent => true)
249
274
  lambda { resource.load_and_authorize_resource }.should raise_error(CanCan::AccessDenied)
250
275
  end
251
276
 
252
277
  it "should load the model using a custom class" do
253
- @params.merge!(:action => "show", :id => 123)
254
- stub(Project).find(123) { :some_project }
278
+ project = Project.create!
279
+ @params.merge!(:action => "show", :id => project.id)
255
280
  resource = CanCan::ControllerResource.new(@controller, :class => Project)
256
281
  resource.load_resource
257
- @controller.instance_variable_get(:@project).should == :some_project
282
+ @controller.instance_variable_get(:@project).should == project
258
283
  end
259
284
 
260
285
  it "should authorize based on resource name if class is false" do
@@ -265,20 +290,20 @@ describe CanCan::ControllerResource do
265
290
  end
266
291
 
267
292
  it "should load and authorize using custom instance name" do
268
- @params.merge!(:action => "show", :id => 123)
269
- stub(Project).find(123) { :some_project }
270
- stub(@controller).authorize!(:show, :some_project) { raise CanCan::AccessDenied }
293
+ project = Project.create!
294
+ @params.merge!(:action => "show", :id => project.id)
295
+ stub(@controller).authorize!(:show, project) { raise CanCan::AccessDenied }
271
296
  resource = CanCan::ControllerResource.new(@controller, :instance_name => :custom_project)
272
297
  lambda { resource.load_and_authorize_resource }.should raise_error(CanCan::AccessDenied)
273
- @controller.instance_variable_get(:@custom_project).should == :some_project
298
+ @controller.instance_variable_get(:@custom_project).should == project
274
299
  end
275
300
 
276
301
  it "should load resource using custom find_by attribute" do
277
- @params.merge!(:action => "show", :id => 123)
278
- stub(Project).find_by_name!(123) { :some_project }
302
+ project = Project.create!(:name => "foo")
303
+ @params.merge!(:action => "show", :id => "foo")
279
304
  resource = CanCan::ControllerResource.new(@controller, :find_by => :name)
280
305
  resource.load_resource
281
- @controller.instance_variable_get(:@project).should == :some_project
306
+ @controller.instance_variable_get(:@project).should == project
282
307
  end
283
308
 
284
309
  it "should raise ImplementationRemoved when adding :name option" do
@@ -1,12 +1,17 @@
1
1
  require 'rubygems'
2
- require 'bundler'
3
- Bundler.require(:default, :test)
2
+ require 'bundler/setup'
3
+ Bundler.require(:default)
4
+ require 'supermodel' # shouldn't Bundler do this already?
4
5
  require 'active_support/all'
5
6
  require 'matchers'
6
7
  require 'cancan/matchers'
7
8
 
8
9
  RSpec.configure do |config|
9
10
  config.mock_with :rr
11
+ config.before(:each) do
12
+ Project.delete_all
13
+ Category.delete_all
14
+ end
10
15
  end
11
16
 
12
17
  class Ability
@@ -16,17 +21,12 @@ class Ability
16
21
  end
17
22
  end
18
23
 
19
- # Generic class to mimic a model
20
- class Project
21
- attr_accessor :name
22
-
23
- def initialize(attributes = {})
24
- @name = attributes[:name]
25
- end
24
+ class Category < SuperModel::Base
25
+ has_many :projects
26
+ end
26
27
 
27
- def attributes=(attributes)
28
- @name = attributes[:name] if attributes[:name]
29
- end
28
+ class Project < SuperModel::Base
29
+ belongs_to :category
30
30
 
31
31
  class << self
32
32
  protected
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cancan
3
3
  version: !ruby/object:Gem::Version
4
- hash: 7
4
+ hash: 5
5
5
  prerelease: false
6
6
  segments:
7
7
  - 1
8
8
  - 4
9
- - 0
10
- version: 1.4.0
9
+ - 1
10
+ version: 1.4.1
11
11
  platform: ruby
12
12
  authors:
13
13
  - Ryan Bates
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-10-05 00:00:00 -07:00
18
+ date: 2010-11-12 00:00:00 -08:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -68,6 +68,22 @@ dependencies:
68
68
  version: 0.10.11
69
69
  type: :development
70
70
  version_requirements: *id003
71
+ - !ruby/object:Gem::Dependency
72
+ name: supermodel
73
+ prerelease: false
74
+ requirement: &id004 !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ~>
78
+ - !ruby/object:Gem::Version
79
+ hash: 19
80
+ segments:
81
+ - 0
82
+ - 1
83
+ - 4
84
+ version: 0.1.4
85
+ type: :development
86
+ version_requirements: *id004
71
87
  description: Simple authorization solution for Rails which is decoupled from user roles. All permissions are stored in a single location.
72
88
  email: ryan@railscasts.com
73
89
  executables: []