marnen-cancan 2.0.0.alpha.pre.f1cebde51a87be149b4970a3287826bb63c0ac0b

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 +15 -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 +265 -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 +535 -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 +227 -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 +197 -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