culturecode-cancan 2.0.0.alpha

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.
Files changed (41) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.rdoc +381 -0
  3. data/Gemfile +3 -0
  4. data/LICENSE +20 -0
  5. data/README.rdoc +108 -0
  6. data/Rakefile +18 -0
  7. data/init.rb +1 -0
  8. data/lib/cancan.rb +13 -0
  9. data/lib/cancan/ability.rb +348 -0
  10. data/lib/cancan/controller_additions.rb +392 -0
  11. data/lib/cancan/controller_resource.rb +266 -0
  12. data/lib/cancan/exceptions.rb +53 -0
  13. data/lib/cancan/inherited_resource.rb +20 -0
  14. data/lib/cancan/matchers.rb +14 -0
  15. data/lib/cancan/model_adapters/abstract_adapter.rb +56 -0
  16. data/lib/cancan/model_adapters/active_record_adapter.rb +172 -0
  17. data/lib/cancan/model_adapters/data_mapper_adapter.rb +34 -0
  18. data/lib/cancan/model_adapters/default_adapter.rb +7 -0
  19. data/lib/cancan/model_adapters/mongoid_adapter.rb +54 -0
  20. data/lib/cancan/model_additions.rb +29 -0
  21. data/lib/cancan/rule.rb +178 -0
  22. data/lib/generators/cancan/ability/USAGE +5 -0
  23. data/lib/generators/cancan/ability/ability_generator.rb +16 -0
  24. data/lib/generators/cancan/ability/templates/ability.rb +24 -0
  25. data/lib/generators/cancan/ability/templates/ability_spec.rb +16 -0
  26. data/lib/generators/cancan/ability/templates/ability_test.rb +10 -0
  27. data/spec/README.rdoc +28 -0
  28. data/spec/cancan/ability_spec.rb +541 -0
  29. data/spec/cancan/controller_additions_spec.rb +118 -0
  30. data/spec/cancan/controller_resource_spec.rb +551 -0
  31. data/spec/cancan/exceptions_spec.rb +58 -0
  32. data/spec/cancan/inherited_resource_spec.rb +58 -0
  33. data/spec/cancan/matchers_spec.rb +33 -0
  34. data/spec/cancan/model_adapters/active_record_adapter_spec.rb +278 -0
  35. data/spec/cancan/model_adapters/data_mapper_adapter_spec.rb +120 -0
  36. data/spec/cancan/model_adapters/default_adapter_spec.rb +7 -0
  37. data/spec/cancan/model_adapters/mongoid_adapter_spec.rb +226 -0
  38. data/spec/cancan/rule_spec.rb +55 -0
  39. data/spec/matchers.rb +13 -0
  40. data/spec/spec_helper.rb +49 -0
  41. metadata +194 -0
@@ -0,0 +1,118 @@
1
+ require "spec_helper"
2
+
3
+ describe CanCan::ControllerAdditions do
4
+ before(:each) do
5
+ @params = HashWithIndifferentAccess.new
6
+ @controller_class = Class.new
7
+ @controller = @controller_class.new
8
+ @controller.stub(:params) { @params }
9
+ @controller.stub(:current_user) { :current_user }
10
+ @controller_class.should_receive(:helper_method).with(:can?, :cannot?, :current_ability)
11
+ @controller_class.send(:include, CanCan::ControllerAdditions)
12
+ end
13
+
14
+ it "raises ImplementationRemoved when attempting to call load/authorize/skip/check calls on a controller" do
15
+ lambda { @controller_class.load_resource }.should raise_error(CanCan::ImplementationRemoved)
16
+ lambda { @controller_class.authorize_resource }.should raise_error(CanCan::ImplementationRemoved)
17
+ lambda { @controller_class.skip_load_resource }.should raise_error(CanCan::ImplementationRemoved)
18
+ lambda { @controller_class.skip_authorize_resource }.should raise_error(CanCan::ImplementationRemoved)
19
+ lambda { @controller_class.check_authorization }.should raise_error(CanCan::ImplementationRemoved)
20
+ lambda { @controller_class.skip_authorization_check }.should raise_error(CanCan::ImplementationRemoved)
21
+ lambda { @controller_class.cancan_skipper }.should raise_error(CanCan::ImplementationRemoved)
22
+ end
23
+
24
+ it "authorize! should pass args to current ability" do
25
+ @controller.current_ability.should_receive(:authorize!).with(:foo, :bar)
26
+ @controller.authorize!(:foo, :bar)
27
+ end
28
+
29
+ it "provides a can? and cannot? methods which go through the current ability" do
30
+ @controller.current_ability.should be_kind_of(Ability)
31
+ @controller.can?(:foo, :bar).should be_false
32
+ @controller.cannot?(:foo, :bar).should be_true
33
+ end
34
+
35
+ it "load_and_authorize_resource adds a before filter which passes call to ControllerResource" do
36
+ controller_resource = double("controller_resource")
37
+ controller_resource.should_receive(:process)
38
+ CanCan::ControllerResource.stub(:new).with(@controller, nil, :load => true, :authorize => true, :foo => :bar) { controller_resource }
39
+ @controller_class.should_receive(:before_filter).with({}).and_yield(@controller)
40
+ @controller_class.load_and_authorize_resource :foo => :bar
41
+ end
42
+
43
+ it "load_and_authorize_resource passes first argument as the resource name" do
44
+ controller_resource = double("controller_resource")
45
+ controller_resource.should_receive(:process)
46
+ CanCan::ControllerResource.stub(:new).with(@controller, :project, :load => true, :authorize => true, :foo => :bar) { controller_resource }
47
+ @controller_class.should_receive(:before_filter).with({}).and_yield(@controller)
48
+ @controller_class.load_and_authorize_resource :project, :foo => :bar
49
+ end
50
+
51
+ it "load_and_authorize_resource passes :only, :except, :if, :unless options to before filter" do
52
+ controller_resource = double("controller_resource")
53
+ controller_resource.should_receive(:process)
54
+ CanCan::ControllerResource.stub(:new).with(@controller, nil, :load => true, :authorize => true) { controller_resource }
55
+ @controller_class.should_receive(:before_filter).with(:only => 1, :except => 2, :if => 3, :unless => 4).and_yield(@controller)
56
+ @controller_class.load_and_authorize_resource :only => 1, :except => 2, :if => 3, :unless => 4
57
+ end
58
+
59
+ it "load_and_authorize_resource with :prepend prepends the before filter" do
60
+ @controller_class.should_receive(:prepend_before_filter).with({})
61
+ @controller_class.load_and_authorize_resource :foo => :bar, :prepend => true
62
+ end
63
+
64
+ it "cancan_resource_class should be ControllerResource by default" do
65
+ @controller.class.cancan_resource_class.should == CanCan::ControllerResource
66
+ end
67
+
68
+ it "cancan_resource_class should be InheritedResource when class includes InheritedResources::Actions" do
69
+ @controller.class.stub(:ancestors) { ["InheritedResources::Actions"] }
70
+ @controller.class.cancan_resource_class.should == CanCan::InheritedResource
71
+ end
72
+
73
+ it "enable_authorization should call authorize! with controller and action name" do
74
+ @params.merge!(:controller => "projects", :action => "create")
75
+ @controller.should_receive(:authorize!).with("create", "projects")
76
+ @controller_class.stub(:before_filter).with(:only => :foo, :except => :bar).and_yield(@controller)
77
+ @controller_class.stub(:after_filter).with(:only => :foo, :except => :bar)
78
+ @controller_class.enable_authorization(:only => :foo, :except => :bar)
79
+ end
80
+
81
+ it "enable_authorization should raise InsufficientAuthorizationCheck when not fully authoried" do
82
+ @params.merge!(:controller => "projects", :action => "create")
83
+ @controller_class.stub(:before_filter).with(:only => :foo, :except => :bar)
84
+ @controller_class.stub(:after_filter).with(:only => :foo, :except => :bar).and_yield(@controller)
85
+ lambda {
86
+ @controller_class.enable_authorization(:only => :foo, :except => :bar)
87
+ }.should raise_error(CanCan::InsufficientAuthorizationCheck)
88
+ end
89
+
90
+ it "enable_authorization should not call authorize! when :if is false" do
91
+ @authorize_called = false
92
+ @controller.stub(:authorize?) { false }
93
+ @controller.stub(:authorize!) { @authorize_called = true }
94
+ @controller_class.should_receive(:before_filter).with({}).and_yield(@controller)
95
+ @controller_class.should_receive(:after_filter).with({}).and_yield(@controller)
96
+ @controller_class.enable_authorization(:if => :authorize?)
97
+ @authorize_called.should be_false
98
+ end
99
+
100
+ it "enable_authorization should not call authorize! when :unless is true" do
101
+ @authorize_called = false
102
+ @controller.stub(:engine_controller?) { true }
103
+ @controller.stub(:authorize!) { @authorize_called = true }
104
+ @controller_class.should_receive(:before_filter).with({}).and_yield(@controller)
105
+ @controller_class.should_receive(:after_filter).with({}).and_yield(@controller)
106
+ @controller_class.enable_authorization(:unless => :engine_controller?)
107
+ @authorize_called.should be_false
108
+ end
109
+
110
+ it "enable_authorization should pass block to rescue_from CanCan::Unauthorized call" do
111
+ @block_called = false
112
+ @controller_class.should_receive(:before_filter).with({})
113
+ @controller_class.should_receive(:after_filter).with({})
114
+ @controller_class.should_receive(:rescue_from).with(CanCan::Unauthorized).and_yield(:exception)
115
+ @controller_class.enable_authorization { |e| @block_called = (e == :exception) }
116
+ @block_called.should be_true
117
+ end
118
+ end
@@ -0,0 +1,551 @@
1
+ require "spec_helper"
2
+
3
+ describe CanCan::ControllerResource do
4
+ before(:each) do
5
+ Project.delete_all
6
+ Category.delete_all
7
+ @params = HashWithIndifferentAccess.new(:controller => "projects")
8
+ @controller_class = Class.new
9
+ @controller = @controller_class.new
10
+ @ability = Ability.new(nil)
11
+ @controller.stub(:params) { @params }
12
+ @controller.stub(:current_ability) { @ability }
13
+ @controller.stub(:authorize!) { |*args| @ability.authorize!(*args) }
14
+ # @controller_class.stub(:cancan_skipper) { {:authorize => {}, :load => {}} }
15
+ end
16
+
17
+ it "loads the resource into an instance variable if params[:id] is specified" do
18
+ project = Project.create!
19
+ @params.merge!(:action => "show", :id => project.id)
20
+ CanCan::ControllerResource.new(@controller, :load => true).process
21
+ @controller.instance_variable_get(:@project).should == project
22
+ end
23
+
24
+ it "does not load resource into an instance variable if already set" do
25
+ @params.merge!(:action => "show", :id => 123)
26
+ @controller.instance_variable_set(:@project, :some_project)
27
+ CanCan::ControllerResource.new(@controller, :load => true).process
28
+ @controller.instance_variable_get(:@project).should == :some_project
29
+ end
30
+
31
+ it "loads resource for namespaced controller" do
32
+ project = Project.create!
33
+ @params.merge!(:controller => "admin/projects", :action => "show", :id => project.id)
34
+ CanCan::ControllerResource.new(@controller, :load => true).process
35
+ @controller.instance_variable_get(:@project).should == project
36
+ end
37
+
38
+ it "attempts to load a resource with the same namespace as the controller when using :: for namespace" do
39
+ module SomeEngine
40
+ class Project < ::Project; end
41
+ end
42
+ project = SomeEngine::Project.create!
43
+ @params.merge!(:controller => "SomeEngine::ProjectsController", :action => "show", :id => project.id)
44
+ CanCan::ControllerResource.new(@controller, :load => true).process
45
+ @controller.instance_variable_get(:@project).should == project
46
+ end
47
+
48
+ # Rails includes namespace in params, see issue #349
49
+ it "creates through the namespaced params" do
50
+ module SomeEngine
51
+ class Project < ::Project; end
52
+ end
53
+ @params.merge!(:controller => "SomeEngine::ProjectsController", :action => "create", :some_engine_project => {:name => "foobar"})
54
+ CanCan::ControllerResource.new(@controller, :load => true).process
55
+ @controller.instance_variable_get(:@project).name.should == "foobar"
56
+ end
57
+
58
+ it "loads resource for namespaced controller when using '::' for namespace" do
59
+ project = Project.create!
60
+ @params.merge!(:controller => "Admin::ProjectsController", :action => "show", :id => project.id)
61
+ CanCan::ControllerResource.new(@controller, :load => true).process
62
+ @controller.instance_variable_get(:@project).should == project
63
+ end
64
+
65
+ it "has the specified nested resource_class when using / for namespace" do
66
+ module Admin
67
+ class Dashboard; end
68
+ end
69
+ @ability.can(:index, "admin/dashboard")
70
+ @params.merge!(:controller => "admin/dashboard", :action => "index")
71
+ @controller.authorize!(:index, "admin/dashboard")
72
+ resource = CanCan::ControllerResource.new(@controller, :authorize => true)
73
+ resource.send(:resource_class).should == Admin::Dashboard
74
+ end
75
+
76
+ it "builds a new resource with hash if params[:id] is not specified and authorize on each attribute" do
77
+ @params.merge!(:action => "create", :project => {:name => "foobar"})
78
+ CanCan::ControllerResource.new(@controller, :load => true).process
79
+ @controller.instance_variable_get(:@project).name.should == "foobar"
80
+ end
81
+
82
+ it "builds a new resource for namespaced model with hash if params[:id] is not specified" do
83
+ module SomeEngine
84
+ class Project < ::Project; end
85
+ end
86
+ @params.merge!(:action => "create", :some_engine_project => {:name => "foobar"})
87
+ CanCan::ControllerResource.new(@controller, :load => true, :class => SomeEngine::Project).process
88
+ @controller.instance_variable_get(:@project).name.should == "foobar"
89
+ end
90
+
91
+ it "builds a new resource with attributes from current ability" do
92
+ @params.merge!(:action => "new")
93
+ @ability.can(:create, :projects, :name => "from conditions")
94
+ CanCan::ControllerResource.new(@controller, :load => true).process
95
+ @controller.instance_variable_get(:@project).name.should == "from conditions"
96
+ end
97
+
98
+ it "overrides initial attributes with params" do
99
+ @params.merge!(:action => "new", :project => {:name => "from params"})
100
+ @ability.can(:create, :projects, :name => "from conditions")
101
+ CanCan::ControllerResource.new(@controller, :load => true).process
102
+ @controller.instance_variable_get(:@project).name.should == "from params"
103
+ end
104
+
105
+ it "builds a collection when on index action when class responds to accessible_by and mark ability as fully authorized" do
106
+ Project.stub(:accessible_by).with(@ability, :index) { :found_projects }
107
+ @params[:action] = "index"
108
+ CanCan::ControllerResource.new(@controller, :project, :load => true).process
109
+ @controller.instance_variable_get(:@project).should be_nil
110
+ @controller.instance_variable_get(:@projects).should == :found_projects
111
+ @ability.should be_fully_authorized(:index, :projects)
112
+ end
113
+
114
+ it "does not build a collection when on index action when class does not respond to accessible_by and not mark ability as fully authorized" do
115
+ class CustomModel
116
+ end
117
+ @params[:controller] = "custom_models"
118
+ @params[:action] = "index"
119
+ CanCan::ControllerResource.new(@controller, :load => true).process
120
+ @controller.instance_variable_get(:@project).should be_nil
121
+ @controller.instance_variable_defined?(:@projects).should be_false
122
+ @ability.should_not be_fully_authorized(:index, :projects)
123
+ end
124
+
125
+ it "does not use accessible_by when defining abilities through a block" do
126
+ Project.stub(:accessible_by).with(@ability) { :found_projects }
127
+ @params[:action] = "index"
128
+ @ability.can(:read, :projects) { |p| false }
129
+ CanCan::ControllerResource.new(@controller, :load => true).process
130
+ @controller.instance_variable_get(:@project).should be_nil
131
+ @controller.instance_variable_defined?(:@projects).should be_false
132
+ end
133
+
134
+ it "does not authorize resource in collection action" do
135
+ @params[:action] = "index"
136
+ @controller.instance_variable_set(:@project, :some_project)
137
+ @controller.stub(:authorize!).with(:index, :projects) { raise CanCan::Unauthorized }
138
+ resource = CanCan::ControllerResource.new(@controller, :authorize => true)
139
+ lambda { resource.process }.should_not raise_error(CanCan::Unauthorized)
140
+ end
141
+
142
+ it "authorizes parent resource in collection action" do
143
+ @params[:action] = "index"
144
+ @controller.instance_variable_set(:@category, :some_category)
145
+ @controller.stub(:authorize!).with(:show, :some_category) { raise CanCan::Unauthorized }
146
+ resource = CanCan::ControllerResource.new(@controller, :category, :parent => true, :authorize => true)
147
+ lambda { resource.process }.should raise_error(CanCan::Unauthorized)
148
+ end
149
+
150
+ it "performs authorization using controller action and loaded model" do
151
+ @params.merge!(:action => "show", :id => 123)
152
+ @controller.instance_variable_set(:@project, :some_project)
153
+ @controller.stub(:authorize!).with(:show, :some_project) { raise CanCan::Unauthorized }
154
+ resource = CanCan::ControllerResource.new(@controller, :authorize => true)
155
+ lambda { resource.process }.should raise_error(CanCan::Unauthorized)
156
+ end
157
+
158
+ it "does not perform authorization using controller action when no loaded model" do
159
+ @params.merge!(:action => "show", :id => 123)
160
+ @controller.stub(:authorize!).with(:show, :projects) { raise CanCan::Unauthorized }
161
+ resource = CanCan::ControllerResource.new(@controller, :authorize => true)
162
+ lambda { resource.process }.should_not raise_error(CanCan::Unauthorized)
163
+ end
164
+
165
+ it "does not build a single resource when on custom collection action even with id" do
166
+ @params.merge!(:action => "sort", :id => 123)
167
+ CanCan::ControllerResource.new(@controller, :load => true, :collection => [:sort, :list]).process
168
+ @controller.instance_variable_get(:@project).should be_nil
169
+ end
170
+
171
+ it "loads a collection resource when on custom action with no id param" do
172
+ Project.stub(:accessible_by).with(@ability, :sort) { :found_projects }
173
+ @params[:action] = "sort"
174
+ CanCan::ControllerResource.new(@controller, :load => true).process
175
+ @controller.instance_variable_get(:@project).should be_nil
176
+ @controller.instance_variable_get(:@projects).should == :found_projects
177
+ end
178
+
179
+ it "builds a resource when on custom new action even when params[:id] exists" do
180
+ @params.merge!(:action => "build", :id => 123)
181
+ Project.stub(:new) { :some_project }
182
+ CanCan::ControllerResource.new(@controller, :load => true, :new => :build).process
183
+ @controller.instance_variable_get(:@project).should == :some_project
184
+ end
185
+
186
+ it "does not try to load resource for other action if params[:id] is undefined" do
187
+ @params[:action] = "list"
188
+ CanCan::ControllerResource.new(@controller, :load => true).process
189
+ @controller.instance_variable_get(:@project).should be_nil
190
+ end
191
+
192
+ it "is a parent resource when name is provided which doesn't match controller" do
193
+ resource = CanCan::ControllerResource.new(@controller, :category)
194
+ resource.should be_parent
195
+ end
196
+
197
+ it "does not be a parent resource when name is provided which matches controller" do
198
+ resource = CanCan::ControllerResource.new(@controller, :project)
199
+ resource.should_not be_parent
200
+ end
201
+
202
+ it "is parent if specified in options" do
203
+ resource = CanCan::ControllerResource.new(@controller, :project, {:parent => true})
204
+ resource.should be_parent
205
+ end
206
+
207
+ it "does not be parent if specified in options" do
208
+ resource = CanCan::ControllerResource.new(@controller, :category, {:parent => false})
209
+ resource.should_not be_parent
210
+ end
211
+
212
+ it "has the specified resource_class if name is passed to load_resource" do
213
+ resource = CanCan::ControllerResource.new(@controller, :category)
214
+ resource.send(:resource_class).should == Category
215
+ end
216
+
217
+ it "loads parent resource through proper id parameter" do
218
+ project = Project.create!
219
+ @params.merge!(:action => "index", :project_id => project.id)
220
+ CanCan::ControllerResource.new(@controller, :project, :load => true, :parent => true).process
221
+ @controller.instance_variable_get(:@project).should == project
222
+ end
223
+
224
+ it "loads resource through the association of another parent resource using instance variable" do
225
+ @params.merge!(:action => "show", :id => 123)
226
+ category = double("category", :projects => double("projects"))
227
+ category.projects.stub(:find).with(123) { :some_project }
228
+ @controller.instance_variable_set(:@category, category)
229
+ CanCan::ControllerResource.new(@controller, :load => true, :through => :category).process
230
+ @controller.instance_variable_get(:@project).should == :some_project
231
+ end
232
+
233
+ it "loads resource through the custom association name" do
234
+ @params.merge!(:action => "show", :id => 123)
235
+ category = double("category", :custom_projects => double("custom_projects"))
236
+ category.custom_projects.stub(:find).with(123) { :some_project }
237
+ @controller.instance_variable_set(:@category, category)
238
+ CanCan::ControllerResource.new(@controller, :load => true, :through => :category, :through_association => :custom_projects).process
239
+ @controller.instance_variable_get(:@project).should == :some_project
240
+ end
241
+
242
+ it "loads resource through the association of another parent resource using method" do
243
+ @params.merge!(:action => "show", :id => 123)
244
+ category = double("category", :projects => double("projects"))
245
+ @controller.stub(:category) { category }
246
+ category.projects.stub(:find).with(123) { :some_project }
247
+ CanCan::ControllerResource.new(@controller, :load => true, :through => :category).process
248
+ @controller.instance_variable_get(:@project).should == :some_project
249
+ end
250
+
251
+ it "does not load through parent resource if instance isn't loaded when shallow" do
252
+ project = Project.create!
253
+ @params.merge!(:action => "show", :id => project.id)
254
+ CanCan::ControllerResource.new(@controller, :load => true, :through => :category, :shallow => true).process
255
+ @controller.instance_variable_get(:@project).should == project
256
+ end
257
+
258
+ it "raises Unauthorized when attempting to load resource through nil" do
259
+ project = Project.create!
260
+ @params.merge!(:action => "show", :id => project.id)
261
+ resource = CanCan::ControllerResource.new(@controller, :load => true, :through => :category)
262
+ lambda {
263
+ resource.process
264
+ }.should raise_error(CanCan::Unauthorized) { |exception|
265
+ exception.action.should == :show
266
+ exception.subject.should == :projects
267
+ }
268
+ @controller.instance_variable_get(:@project).should be_nil
269
+ end
270
+
271
+ it "named resources should be loaded independently of the controller name" do
272
+ category = Category.create!
273
+ @params.merge!(:action => "new", :category_id => category.id)
274
+ CanCan::ControllerResource.new(@controller, :category, :load => true).process
275
+ CanCan::ControllerResource.new(@controller, :project, :load => true, :through => :category).process
276
+ @controller.instance_variable_get(:@category).should eq(category)
277
+ project = @controller.instance_variable_get(:@project)
278
+ project.category.should eq(category)
279
+ end
280
+
281
+ it "parent resources shouldn't be altered" do
282
+ category = Category.create!
283
+ @params.merge!(:action => "create", :category_id => category.id, :project => { :name => 'foo' })
284
+ CanCan::ControllerResource.new(@controller, :category, :load => true).process
285
+ CanCan::ControllerResource.new(@controller, :project, :load => true, :through => :category).process
286
+ project = @controller.instance_variable_get(:@project)
287
+ project.should be_new_record
288
+ project.name.should eq('foo')
289
+ end
290
+
291
+ it "does not overrides an attribute if it is based on parent resource" do
292
+ user = double('user')
293
+ user.should_receive(:category_id).and_return nil
294
+ @ability = Ability.new user
295
+ @ability.can :new, :projects, :category_id => user.category_id
296
+ category = Category.create!
297
+ @controller.instance_variable_set(:@category, category)
298
+
299
+ @params.merge! :action => 'new', :category_id => category.id
300
+ CanCan::ControllerResource.new(@controller, :category, :load => true).process
301
+ CanCan::ControllerResource.new(@controller, :project, :load => true, :through => :category, :singleton => true).process
302
+ project = @controller.instance_variable_get(:@project)
303
+ project.category_id.should_not be_nil
304
+ project.category.should eq(category)
305
+ end
306
+
307
+ it "authorizes nested resource through parent association on index action" do
308
+ pending
309
+ @params.merge!(:action => "index")
310
+ category = Object.new
311
+ @controller.instance_variable_set(:@category, category)
312
+ @controller.stub(:authorize!).with(:index, category => :projects) { raise CanCan::Unauthorized }
313
+ resource = CanCan::ControllerResource.new(@controller, :authorize => true, :through => :category)
314
+ lambda { resource.process }.should raise_error(CanCan::Unauthorized)
315
+ end
316
+
317
+ it "loads through first matching if multiple are given" do
318
+ @params.merge!(:action => "show", :id => 123)
319
+ category = double("category", :projects => double("projects"))
320
+ category.projects.stub(:find).with(123) { :some_project }
321
+ @controller.instance_variable_set(:@category, category)
322
+ CanCan::ControllerResource.new(@controller, :load => true, :through => [:category, :user]).process
323
+ @controller.instance_variable_get(:@project).should == :some_project
324
+ end
325
+
326
+ it "finds record through has_one association with :singleton option without id param" do
327
+ @params.merge!(:action => "show", :id => nil)
328
+ category = Object.new
329
+ @controller.instance_variable_set(:@category, category)
330
+ category.stub(:project) { :some_project }
331
+ CanCan::ControllerResource.new(@controller, :load => true, :through => :category, :singleton => true).process
332
+ @controller.instance_variable_get(:@project).should == :some_project
333
+ end
334
+
335
+ it "does not build record through has_one association with :singleton option because it can cause it to delete it in the database" do
336
+ @params.merge!(:action => "create", :project => {:name => "foobar"})
337
+ category = Category.new
338
+ @controller.instance_variable_set(:@category, category)
339
+ CanCan::ControllerResource.new(@controller, :load => true, :through => :category, :singleton => true).process
340
+ @controller.instance_variable_get(:@project).name.should == "foobar"
341
+ @controller.instance_variable_get(:@project).category.should == category
342
+ end
343
+
344
+ it "finds record through has_one association with :singleton and :shallow options" do
345
+ project = Project.create!
346
+ @params.merge!(:action => "show", :id => project.id)
347
+ CanCan::ControllerResource.new(@controller, :load => true, :through => :category, :singleton => true, :shallow => true).process
348
+ @controller.instance_variable_get(:@project).should == project
349
+ end
350
+
351
+ it "builds record through has_one association with :singleton and :shallow options" do
352
+ @params.merge!(:action => "create", :project => {:name => "foobar"})
353
+ CanCan::ControllerResource.new(@controller, :load => true, :through => :category, :singleton => true, :shallow => true).process
354
+ @controller.instance_variable_get(:@project).name.should == "foobar"
355
+ end
356
+
357
+ it "only authorizes :show action on parent resource" do
358
+ project = Project.create!
359
+ @params.merge!(:action => "new", :project_id => project.id)
360
+ @controller.stub(:authorize!).with(:show, project) { raise CanCan::Unauthorized }
361
+ resource = CanCan::ControllerResource.new(@controller, :project, :load => true, :authorize => true, :parent => true)
362
+ lambda { resource.process }.should raise_error(CanCan::Unauthorized)
363
+ end
364
+
365
+ it "authorizes update action before setting attributes" do
366
+ @ability.can :update, :projects, :name => "bar"
367
+ project = Project.create!(:name => "foo")
368
+ @params.merge!(:action => "update", :id => project.id, :project => {:name => "bar"})
369
+ resource = CanCan::ControllerResource.new(@controller, :project, :load => true, :authorize => true)
370
+ lambda { resource.process }.should raise_error(CanCan::Unauthorized)
371
+ end
372
+
373
+ it "authorizes update action after setting attributes" do
374
+ @ability.can :update, :projects, :name => "foo"
375
+ project = Project.create!(:name => "foo")
376
+ @params.merge!(:action => "update", :id => project.id, :project => {:name => "bar"})
377
+ resource = CanCan::ControllerResource.new(@controller, :project, :load => true, :authorize => true)
378
+ lambda { resource.process }.should raise_error(CanCan::Unauthorized)
379
+ end
380
+
381
+ it "loads the model using a custom class" do
382
+ project = Project.create!
383
+ @params.merge!(:action => "show", :id => project.id)
384
+ CanCan::ControllerResource.new(@controller, :load => true, :class => Project).process
385
+ @controller.instance_variable_get(:@project).should == project
386
+ end
387
+
388
+ it "loads the model using a custom namespaced class" do
389
+ module SomeEngine
390
+ class Project < ::Project; end
391
+ end
392
+ project = SomeEngine::Project.create!
393
+ @params.merge!(:action => "show", :id => project.id)
394
+ CanCan::ControllerResource.new(@controller, :load => true, :class => SomeEngine::Project).process
395
+ @controller.instance_variable_get(:@project).should == project
396
+ end
397
+
398
+ it "does not authorize based on resource name if class is false because we don't do class level authorization anymore" do
399
+ @params.merge!(:action => "show", :id => 123)
400
+ @controller.stub(:authorize!).with(:show, :projects) { raise CanCan::Unauthorized }
401
+ resource = CanCan::ControllerResource.new(@controller, :authorize => true, :class => false)
402
+ lambda { resource.process }.should_not raise_error(CanCan::Unauthorized)
403
+ end
404
+
405
+ it "loads and authorize using custom instance name" do
406
+ project = Project.create!
407
+ @params.merge!(:action => "show", :id => project.id)
408
+ @controller.stub(:authorize!).with(:show, project) { raise CanCan::Unauthorized }
409
+ resource = CanCan::ControllerResource.new(@controller, :load => true, :authorize => true, :instance_name => :custom_project)
410
+ lambda { resource.process }.should raise_error(CanCan::Unauthorized)
411
+ @controller.instance_variable_get(:@custom_project).should == project
412
+ end
413
+
414
+ it "loads resource using custom ID param" do
415
+ project = Project.create!
416
+ @params.merge!(:action => "show", :the_project => project.id)
417
+ resource = CanCan::ControllerResource.new(@controller, :id_param => :the_project, :load => true)
418
+ resource.process
419
+ @controller.instance_variable_get(:@project).should == project
420
+ end
421
+
422
+ it "loads resource using custom find_by attribute" do
423
+ project = Project.create!(:name => "foo")
424
+ @params.merge!(:action => "show", :id => "foo")
425
+ CanCan::ControllerResource.new(@controller, :load => true, :find_by => :name).process
426
+ @controller.instance_variable_get(:@project).should == project
427
+ end
428
+
429
+ it "authorizes each new attribute in the create action" do
430
+ @params.merge!(:action => "create", :project => {:name => "foo"})
431
+ @controller.instance_variable_set(:@project, :some_project)
432
+ @ability.should_receive(:authorize!).with(:create, :some_project, :name)
433
+ CanCan::ControllerResource.new(@controller, :authorize => true).process
434
+ end
435
+
436
+ it "allows full find method to be passed into find_by option" do
437
+ project = Project.create!(:name => "foo")
438
+ @params.merge!(:action => "show", :id => "foo")
439
+ CanCan::ControllerResource.new(@controller, :find_by => :find_by_name, :load => true).process
440
+ @controller.instance_variable_get(:@project).should == project
441
+ end
442
+
443
+ it "authorizes each new attribute in the update action" do
444
+ @params.merge!(:action => "update", :id => 123, :project => {:name => "foo"})
445
+ @controller.instance_variable_set(:@project, :some_project)
446
+ @ability.should_receive(:authorize!).with(:update, :some_project, :name)
447
+ CanCan::ControllerResource.new(@controller, :authorize => true).process
448
+ end
449
+
450
+ it "fetches member through method when instance variable is not provided" do
451
+ @controller.stub(:project) { :some_project }
452
+ @params.merge!(:action => "show", :id => 123)
453
+ @controller.stub(:authorize!).with(:show, :some_project) { raise CanCan::Unauthorized }
454
+ resource = CanCan::ControllerResource.new(@controller, :authorize => true)
455
+ lambda { resource.process }.should raise_error(CanCan::Unauthorized)
456
+ end
457
+
458
+ it "attempts to load a resource with the same namespace as the controller when using :: for namespace" do
459
+ module Namespaced
460
+ class Project < ::Project; end
461
+ end
462
+ project = Namespaced::Project.create!
463
+ @params.merge!(:controller => "Namespaced::ProjectsController", :action => "show", :id => project.id)
464
+ CanCan::ControllerResource.new(@controller, :load => true).process
465
+ @controller.instance_variable_get(:@project).should == project
466
+ end
467
+
468
+ # Rails includes namespace in params, see issue #349
469
+ it "creates through namespaced params" do
470
+ module Namespaced
471
+ class Project < ::Project; end
472
+ end
473
+ @params.merge!(:controller => "Namespaced::ProjectsController", :action => "create", :namespaced_project => {:name => "foobar"})
474
+ CanCan::ControllerResource.new(@controller, :load => true).process
475
+ @controller.instance_variable_get(:@project).name.should == "foobar"
476
+ end
477
+
478
+ it "should properly authorize resource for namespaced controller" do
479
+ @ability.can(:index, "admin/dashboard")
480
+ @params.merge!(:controller => "admin/dashboard", :action => "index")
481
+ @controller.authorize!(:index, "admin/dashboard")
482
+ resource = CanCan::ControllerResource.new(@controller, :authorize => true).process
483
+ lambda { resource.process }.should_not raise_error(CanCan::Unauthorized)
484
+ end
485
+
486
+ # it "raises ImplementationRemoved when adding :name option" do
487
+ # lambda {
488
+ # CanCan::ControllerResource.new(@controller, :name => :foo)
489
+ # }.should raise_error(CanCan::ImplementationRemoved)
490
+ # end
491
+ #
492
+ # it "raises ImplementationRemoved exception when specifying :resource option since it is no longer used" do
493
+ # lambda {
494
+ # CanCan::ControllerResource.new(@controller, :resource => Project)
495
+ # }.should raise_error(CanCan::ImplementationRemoved)
496
+ # end
497
+ #
498
+ # it "raises ImplementationRemoved exception when passing :nested option" do
499
+ # lambda {
500
+ # CanCan::ControllerResource.new(@controller, :nested => :project)
501
+ # }.should raise_error(CanCan::ImplementationRemoved)
502
+ # end
503
+
504
+ # it "skips resource behavior for :only actions in array" do
505
+ # @controller_class.stub(:cancan_skipper) { {:load => {nil => {:only => [:index, :show]}}} }
506
+ # @params.merge!(:action => "index")
507
+ # CanCan::ControllerResource.new(@controller).skip?(:load).should be_true
508
+ # CanCan::ControllerResource.new(@controller, :some_resource).skip?(:load).should be_false
509
+ # @params.merge!(:action => "show")
510
+ # CanCan::ControllerResource.new(@controller).skip?(:load).should be_true
511
+ # @params.merge!(:action => "other_action")
512
+ # CanCan::ControllerResource.new(@controller).skip?(:load).should be_false
513
+ # end
514
+ #
515
+ # it "skips resource behavior for :only one action on resource" do
516
+ # @controller_class.stub(:cancan_skipper) { {:authorize => {:project => {:only => :index}}} }
517
+ # @params.merge!(:action => "index")
518
+ # CanCan::ControllerResource.new(@controller).skip?(:authorize).should be_false
519
+ # CanCan::ControllerResource.new(@controller, :project).skip?(:authorize).should be_true
520
+ # @params.merge!(:action => "other_action")
521
+ # CanCan::ControllerResource.new(@controller, :project).skip?(:authorize).should be_false
522
+ # end
523
+ #
524
+ # it "skips resource behavior :except actions in array" do
525
+ # @controller_class.stub(:cancan_skipper) { {:load => {nil => {:except => [:index, :show]}}} }
526
+ # @params.merge!(:action => "index")
527
+ # CanCan::ControllerResource.new(@controller).skip?(:load).should be_false
528
+ # @params.merge!(:action => "show")
529
+ # CanCan::ControllerResource.new(@controller).skip?(:load).should be_false
530
+ # @params.merge!(:action => "other_action")
531
+ # CanCan::ControllerResource.new(@controller).skip?(:load).should be_true
532
+ # CanCan::ControllerResource.new(@controller, :some_resource).skip?(:load).should be_false
533
+ # end
534
+ #
535
+ # it "skips resource behavior :except one action on resource" do
536
+ # @controller_class.stub(:cancan_skipper) { {:authorize => {:project => {:except => :index}}} }
537
+ # @params.merge!(:action => "index")
538
+ # CanCan::ControllerResource.new(@controller, :project).skip?(:authorize).should be_false
539
+ # @params.merge!(:action => "other_action")
540
+ # CanCan::ControllerResource.new(@controller).skip?(:authorize).should be_false
541
+ # CanCan::ControllerResource.new(@controller, :project).skip?(:authorize).should be_true
542
+ # end
543
+ #
544
+ # it "skips loading and authorization" do
545
+ # @controller_class.stub(:cancan_skipper) { {:authorize => {nil => {}}, :load => {nil => {}}} }
546
+ # @params.merge!(:action => "new")
547
+ # resource = CanCan::ControllerResource.new(@controller)
548
+ # lambda { resource.load_and_authorize_resource }.should_not raise_error
549
+ # @controller.instance_variable_get(:@project).should be_nil
550
+ # end
551
+ end