cancan-2 2.0.0.alpha

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/CHANGELOG.rdoc +381 -0
  2. data/Gemfile +3 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +108 -0
  5. data/Rakefile +18 -0
  6. data/init.rb +1 -0
  7. data/lib/cancan.rb +13 -0
  8. data/lib/cancan/ability.rb +348 -0
  9. data/lib/cancan/controller_additions.rb +392 -0
  10. data/lib/cancan/controller_resource.rb +265 -0
  11. data/lib/cancan/exceptions.rb +53 -0
  12. data/lib/cancan/inherited_resource.rb +20 -0
  13. data/lib/cancan/matchers.rb +14 -0
  14. data/lib/cancan/model_adapters/abstract_adapter.rb +56 -0
  15. data/lib/cancan/model_adapters/active_record_adapter.rb +172 -0
  16. data/lib/cancan/model_adapters/data_mapper_adapter.rb +34 -0
  17. data/lib/cancan/model_adapters/default_adapter.rb +7 -0
  18. data/lib/cancan/model_adapters/mongoid_adapter.rb +54 -0
  19. data/lib/cancan/model_additions.rb +29 -0
  20. data/lib/cancan/rule.rb +178 -0
  21. data/lib/generators/cancan/ability/USAGE +5 -0
  22. data/lib/generators/cancan/ability/ability_generator.rb +16 -0
  23. data/lib/generators/cancan/ability/templates/ability.rb +24 -0
  24. data/lib/generators/cancan/ability/templates/ability_spec.rb +16 -0
  25. data/lib/generators/cancan/ability/templates/ability_test.rb +10 -0
  26. data/spec/README.rdoc +28 -0
  27. data/spec/cancan/ability_spec.rb +541 -0
  28. data/spec/cancan/controller_additions_spec.rb +118 -0
  29. data/spec/cancan/controller_resource_spec.rb +535 -0
  30. data/spec/cancan/exceptions_spec.rb +58 -0
  31. data/spec/cancan/inherited_resource_spec.rb +58 -0
  32. data/spec/cancan/matchers_spec.rb +33 -0
  33. data/spec/cancan/model_adapters/active_record_adapter_spec.rb +278 -0
  34. data/spec/cancan/model_adapters/data_mapper_adapter_spec.rb +120 -0
  35. data/spec/cancan/model_adapters/default_adapter_spec.rb +7 -0
  36. data/spec/cancan/model_adapters/mongoid_adapter_spec.rb +227 -0
  37. data/spec/cancan/rule_spec.rb +55 -0
  38. data/spec/matchers.rb +13 -0
  39. data/spec/spec_helper.rb +49 -0
  40. metadata +212 -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,535 @@
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.new_record?.should eq(true)
288
+ project.name.should eq('foo')
289
+ end
290
+
291
+ it "authorizes nested resource through parent association on index action" do
292
+ pending
293
+ @params.merge!(:action => "index")
294
+ category = Object.new
295
+ @controller.instance_variable_set(:@category, category)
296
+ @controller.stub(:authorize!).with(:index, category => :projects) { raise CanCan::Unauthorized }
297
+ resource = CanCan::ControllerResource.new(@controller, :authorize => true, :through => :category)
298
+ lambda { resource.process }.should raise_error(CanCan::Unauthorized)
299
+ end
300
+
301
+ it "loads through first matching if multiple are given" do
302
+ @params.merge!(:action => "show", :id => 123)
303
+ category = double("category", :projects => double("projects"))
304
+ category.projects.stub(:find).with(123) { :some_project }
305
+ @controller.instance_variable_set(:@category, category)
306
+ CanCan::ControllerResource.new(@controller, :load => true, :through => [:category, :user]).process
307
+ @controller.instance_variable_get(:@project).should == :some_project
308
+ end
309
+
310
+ it "finds record through has_one association with :singleton option without id param" do
311
+ @params.merge!(:action => "show", :id => nil)
312
+ category = Object.new
313
+ @controller.instance_variable_set(:@category, category)
314
+ category.stub(:project) { :some_project }
315
+ CanCan::ControllerResource.new(@controller, :load => true, :through => :category, :singleton => true).process
316
+ @controller.instance_variable_get(:@project).should == :some_project
317
+ end
318
+
319
+ it "does not build record through has_one association with :singleton option because it can cause it to delete it in the database" do
320
+ @params.merge!(:action => "create", :project => {:name => "foobar"})
321
+ category = Category.new
322
+ @controller.instance_variable_set(:@category, category)
323
+ CanCan::ControllerResource.new(@controller, :load => true, :through => :category, :singleton => true).process
324
+ @controller.instance_variable_get(:@project).name.should == "foobar"
325
+ @controller.instance_variable_get(:@project).category.should == category
326
+ end
327
+
328
+ it "finds record through has_one association with :singleton and :shallow options" do
329
+ project = Project.create!
330
+ @params.merge!(:action => "show", :id => project.id)
331
+ CanCan::ControllerResource.new(@controller, :load => true, :through => :category, :singleton => true, :shallow => true).process
332
+ @controller.instance_variable_get(:@project).should == project
333
+ end
334
+
335
+ it "builds record through has_one association with :singleton and :shallow options" do
336
+ @params.merge!(:action => "create", :project => {:name => "foobar"})
337
+ CanCan::ControllerResource.new(@controller, :load => true, :through => :category, :singleton => true, :shallow => true).process
338
+ @controller.instance_variable_get(:@project).name.should == "foobar"
339
+ end
340
+
341
+ it "only authorizes :show action on parent resource" do
342
+ project = Project.create!
343
+ @params.merge!(:action => "new", :project_id => project.id)
344
+ @controller.stub(:authorize!).with(:show, project) { raise CanCan::Unauthorized }
345
+ resource = CanCan::ControllerResource.new(@controller, :project, :load => true, :authorize => true, :parent => true)
346
+ lambda { resource.process }.should raise_error(CanCan::Unauthorized)
347
+ end
348
+
349
+ it "authorizes update action before setting attributes" do
350
+ @ability.can :update, :projects, :name => "bar"
351
+ project = Project.create!(:name => "foo")
352
+ @params.merge!(:action => "update", :id => project.id, :project => {:name => "bar"})
353
+ resource = CanCan::ControllerResource.new(@controller, :project, :load => true, :authorize => true)
354
+ lambda { resource.process }.should raise_error(CanCan::Unauthorized)
355
+ end
356
+
357
+ it "authorizes update action after setting attributes" do
358
+ @ability.can :update, :projects, :name => "foo"
359
+ project = Project.create!(:name => "foo")
360
+ @params.merge!(:action => "update", :id => project.id, :project => {:name => "bar"})
361
+ resource = CanCan::ControllerResource.new(@controller, :project, :load => true, :authorize => true)
362
+ lambda { resource.process }.should raise_error(CanCan::Unauthorized)
363
+ end
364
+
365
+ it "loads the model using a custom class" do
366
+ project = Project.create!
367
+ @params.merge!(:action => "show", :id => project.id)
368
+ CanCan::ControllerResource.new(@controller, :load => true, :class => Project).process
369
+ @controller.instance_variable_get(:@project).should == project
370
+ end
371
+
372
+ it "loads the model using a custom namespaced class" do
373
+ module SomeEngine
374
+ class Project < ::Project; end
375
+ end
376
+ project = SomeEngine::Project.create!
377
+ @params.merge!(:action => "show", :id => project.id)
378
+ CanCan::ControllerResource.new(@controller, :load => true, :class => SomeEngine::Project).process
379
+ @controller.instance_variable_get(:@project).should == project
380
+ end
381
+
382
+ it "does not authorize based on resource name if class is false because we don't do class level authorization anymore" do
383
+ @params.merge!(:action => "show", :id => 123)
384
+ @controller.stub(:authorize!).with(:show, :projects) { raise CanCan::Unauthorized }
385
+ resource = CanCan::ControllerResource.new(@controller, :authorize => true, :class => false)
386
+ lambda { resource.process }.should_not raise_error(CanCan::Unauthorized)
387
+ end
388
+
389
+ it "loads and authorize using custom instance name" do
390
+ project = Project.create!
391
+ @params.merge!(:action => "show", :id => project.id)
392
+ @controller.stub(:authorize!).with(:show, project) { raise CanCan::Unauthorized }
393
+ resource = CanCan::ControllerResource.new(@controller, :load => true, :authorize => true, :instance_name => :custom_project)
394
+ lambda { resource.process }.should raise_error(CanCan::Unauthorized)
395
+ @controller.instance_variable_get(:@custom_project).should == project
396
+ end
397
+
398
+ it "loads resource using custom ID param" do
399
+ project = Project.create!
400
+ @params.merge!(:action => "show", :the_project => project.id)
401
+ resource = CanCan::ControllerResource.new(@controller, :id_param => :the_project, :load => true)
402
+ resource.process
403
+ @controller.instance_variable_get(:@project).should == project
404
+ end
405
+
406
+ it "loads resource using custom find_by attribute" do
407
+ project = Project.create!(:name => "foo")
408
+ @params.merge!(:action => "show", :id => "foo")
409
+ CanCan::ControllerResource.new(@controller, :load => true, :find_by => :name).process
410
+ @controller.instance_variable_get(:@project).should == project
411
+ end
412
+
413
+ it "authorizes each new attribute in the create action" do
414
+ @params.merge!(:action => "create", :project => {:name => "foo"})
415
+ @controller.instance_variable_set(:@project, :some_project)
416
+ @ability.should_receive(:authorize!).with(:create, :some_project, :name)
417
+ CanCan::ControllerResource.new(@controller, :authorize => true).process
418
+ end
419
+
420
+ it "allows full find method to be passed into find_by option" do
421
+ project = Project.create!(:name => "foo")
422
+ @params.merge!(:action => "show", :id => "foo")
423
+ CanCan::ControllerResource.new(@controller, :find_by => :find_by_name, :load => true).process
424
+ @controller.instance_variable_get(:@project).should == project
425
+ end
426
+
427
+ it "authorizes each new attribute in the update action" do
428
+ @params.merge!(:action => "update", :id => 123, :project => {:name => "foo"})
429
+ @controller.instance_variable_set(:@project, :some_project)
430
+ @ability.should_receive(:authorize!).with(:update, :some_project, :name)
431
+ CanCan::ControllerResource.new(@controller, :authorize => true).process
432
+ end
433
+
434
+ it "fetches member through method when instance variable is not provided" do
435
+ @controller.stub(:project) { :some_project }
436
+ @params.merge!(:action => "show", :id => 123)
437
+ @controller.stub(:authorize!).with(:show, :some_project) { raise CanCan::Unauthorized }
438
+ resource = CanCan::ControllerResource.new(@controller, :authorize => true)
439
+ lambda { resource.process }.should raise_error(CanCan::Unauthorized)
440
+ end
441
+
442
+ it "attempts to load a resource with the same namespace as the controller when using :: for namespace" do
443
+ module Namespaced
444
+ class Project < ::Project; end
445
+ end
446
+ project = Namespaced::Project.create!
447
+ @params.merge!(:controller => "Namespaced::ProjectsController", :action => "show", :id => project.id)
448
+ CanCan::ControllerResource.new(@controller, :load => true).process
449
+ @controller.instance_variable_get(:@project).should == project
450
+ end
451
+
452
+ # Rails includes namespace in params, see issue #349
453
+ it "creates through namespaced params" do
454
+ module Namespaced
455
+ class Project < ::Project; end
456
+ end
457
+ @params.merge!(:controller => "Namespaced::ProjectsController", :action => "create", :namespaced_project => {:name => "foobar"})
458
+ CanCan::ControllerResource.new(@controller, :load => true).process
459
+ @controller.instance_variable_get(:@project).name.should == "foobar"
460
+ end
461
+
462
+ it "should properly authorize resource for namespaced controller" do
463
+ @ability.can(:index, "admin/dashboard")
464
+ @params.merge!(:controller => "admin/dashboard", :action => "index")
465
+ @controller.authorize!(:index, "admin/dashboard")
466
+ resource = CanCan::ControllerResource.new(@controller, :authorize => true).process
467
+ lambda { resource.process }.should_not raise_error(CanCan::Unauthorized)
468
+ end
469
+
470
+ # it "raises ImplementationRemoved when adding :name option" do
471
+ # lambda {
472
+ # CanCan::ControllerResource.new(@controller, :name => :foo)
473
+ # }.should raise_error(CanCan::ImplementationRemoved)
474
+ # end
475
+ #
476
+ # it "raises ImplementationRemoved exception when specifying :resource option since it is no longer used" do
477
+ # lambda {
478
+ # CanCan::ControllerResource.new(@controller, :resource => Project)
479
+ # }.should raise_error(CanCan::ImplementationRemoved)
480
+ # end
481
+ #
482
+ # it "raises ImplementationRemoved exception when passing :nested option" do
483
+ # lambda {
484
+ # CanCan::ControllerResource.new(@controller, :nested => :project)
485
+ # }.should raise_error(CanCan::ImplementationRemoved)
486
+ # end
487
+
488
+ # it "skips resource behavior for :only actions in array" do
489
+ # @controller_class.stub(:cancan_skipper) { {:load => {nil => {:only => [:index, :show]}}} }
490
+ # @params.merge!(:action => "index")
491
+ # CanCan::ControllerResource.new(@controller).skip?(:load).should be_true
492
+ # CanCan::ControllerResource.new(@controller, :some_resource).skip?(:load).should be_false
493
+ # @params.merge!(:action => "show")
494
+ # CanCan::ControllerResource.new(@controller).skip?(:load).should be_true
495
+ # @params.merge!(:action => "other_action")
496
+ # CanCan::ControllerResource.new(@controller).skip?(:load).should be_false
497
+ # end
498
+ #
499
+ # it "skips resource behavior for :only one action on resource" do
500
+ # @controller_class.stub(:cancan_skipper) { {:authorize => {:project => {:only => :index}}} }
501
+ # @params.merge!(:action => "index")
502
+ # CanCan::ControllerResource.new(@controller).skip?(:authorize).should be_false
503
+ # CanCan::ControllerResource.new(@controller, :project).skip?(:authorize).should be_true
504
+ # @params.merge!(:action => "other_action")
505
+ # CanCan::ControllerResource.new(@controller, :project).skip?(:authorize).should be_false
506
+ # end
507
+ #
508
+ # it "skips resource behavior :except actions in array" do
509
+ # @controller_class.stub(:cancan_skipper) { {:load => {nil => {:except => [:index, :show]}}} }
510
+ # @params.merge!(:action => "index")
511
+ # CanCan::ControllerResource.new(@controller).skip?(:load).should be_false
512
+ # @params.merge!(:action => "show")
513
+ # CanCan::ControllerResource.new(@controller).skip?(:load).should be_false
514
+ # @params.merge!(:action => "other_action")
515
+ # CanCan::ControllerResource.new(@controller).skip?(:load).should be_true
516
+ # CanCan::ControllerResource.new(@controller, :some_resource).skip?(:load).should be_false
517
+ # end
518
+ #
519
+ # it "skips resource behavior :except one action on resource" do
520
+ # @controller_class.stub(:cancan_skipper) { {:authorize => {:project => {:except => :index}}} }
521
+ # @params.merge!(:action => "index")
522
+ # CanCan::ControllerResource.new(@controller, :project).skip?(:authorize).should be_false
523
+ # @params.merge!(:action => "other_action")
524
+ # CanCan::ControllerResource.new(@controller).skip?(:authorize).should be_false
525
+ # CanCan::ControllerResource.new(@controller, :project).skip?(:authorize).should be_true
526
+ # end
527
+ #
528
+ # it "skips loading and authorization" do
529
+ # @controller_class.stub(:cancan_skipper) { {:authorize => {nil => {}}, :load => {nil => {}}} }
530
+ # @params.merge!(:action => "new")
531
+ # resource = CanCan::ControllerResource.new(@controller)
532
+ # lambda { resource.load_and_authorize_resource }.should_not raise_error
533
+ # @controller.instance_variable_get(:@project).should be_nil
534
+ # end
535
+ end