cancan 1.4.0 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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: []