cancan 1.4.1 → 1.5.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. data/CHANGELOG.rdoc +21 -0
  2. data/Gemfile +17 -0
  3. data/LICENSE +1 -1
  4. data/README.rdoc +16 -77
  5. data/Rakefile +8 -0
  6. data/lib/cancan.rb +8 -3
  7. data/lib/cancan/ability.rb +24 -26
  8. data/lib/cancan/controller_additions.rb +50 -0
  9. data/lib/cancan/controller_resource.rb +33 -15
  10. data/lib/cancan/exceptions.rb +3 -0
  11. data/lib/cancan/model_adapters/abstract_adapter.rb +40 -0
  12. data/lib/cancan/model_adapters/active_record_adapter.rb +119 -0
  13. data/lib/cancan/model_adapters/data_mapper_adapter.rb +33 -0
  14. data/lib/cancan/model_adapters/default_adapter.rb +7 -0
  15. data/lib/cancan/model_adapters/mongoid_adapter.rb +41 -0
  16. data/lib/cancan/{active_record_additions.rb → model_additions.rb} +5 -16
  17. data/lib/cancan/{can_definition.rb → rule.rb} +29 -25
  18. data/lib/generators/cancan/ability/USAGE +4 -0
  19. data/lib/generators/cancan/ability/ability_generator.rb +11 -0
  20. data/lib/generators/cancan/ability/templates/ability.rb +28 -0
  21. data/spec/README.rdoc +28 -0
  22. data/spec/cancan/ability_spec.rb +11 -3
  23. data/spec/cancan/controller_additions_spec.rb +30 -0
  24. data/spec/cancan/controller_resource_spec.rb +68 -2
  25. data/spec/cancan/inherited_resource_spec.rb +3 -1
  26. data/spec/cancan/model_adapters/active_record_adapter_spec.rb +185 -0
  27. data/spec/cancan/model_adapters/data_mapper_adapter_spec.rb +115 -0
  28. data/spec/cancan/model_adapters/default_adapter_spec.rb +7 -0
  29. data/spec/cancan/model_adapters/mongoid_adapter_spec.rb +168 -0
  30. data/spec/cancan/rule_spec.rb +39 -0
  31. data/spec/spec_helper.rb +2 -24
  32. metadata +26 -17
  33. data/lib/cancan/query.rb +0 -97
  34. data/spec/cancan/active_record_additions_spec.rb +0 -75
  35. data/spec/cancan/can_definition_spec.rb +0 -57
  36. data/spec/cancan/query_spec.rb +0 -107
data/CHANGELOG.rdoc CHANGED
@@ -1,3 +1,24 @@
1
+ 1.5.0 (not yet released)
2
+
3
+ * Added an Ability generator - see issue #170
4
+
5
+ * Added DataMapper support (thanks natemueller)
6
+
7
+ * Added Mongoid support (thanks bowsersenior)
8
+
9
+ * Added skip_load_and_authorize_resource methods to controller class - see issue #164
10
+
11
+ * Added support for uncountable resources in index action - see issue #193
12
+
13
+ * Cleaned up README and added spec/README
14
+
15
+ * Internal: renamed CanDefinition to Rule
16
+
17
+ * Internal: added a model adapter layer for easily supporting more ORMs
18
+
19
+ * Internal: added .rvmrc to auto-switch to 1.8.7 with gemset - see issue #231
20
+
21
+
1
22
  1.4.1 (November 12, 2010)
2
23
 
3
24
  * Renaming skip_authorization to skip_authorization_check - see issue #169
data/Gemfile CHANGED
@@ -1,2 +1,19 @@
1
1
  source "http://rubygems.org"
2
+
3
+ case ENV["MODEL_ADAPTER"]
4
+ when nil, "active_record"
5
+ gem "sqlite3-ruby", :require => "sqlite3"
6
+ gem "activerecord", :require => "active_record"
7
+ gem "with_model"
8
+ when "data_mapper"
9
+ gem "dm-core", "~> 1.0.2"
10
+ gem "dm-sqlite-adapter", "~> 1.0.2"
11
+ gem "dm-migrations", "~> 1.0.2"
12
+ when "mongoid"
13
+ gem "bson_ext", "~> 1.1"
14
+ gem "mongoid", "~> 2.0.0.beta.19"
15
+ else
16
+ raise "Unknown model adapter: #{ENV["MODEL_ADAPTER"]}"
17
+ end
18
+
2
19
  gemspec
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2010 Ryan Bates
1
+ Copyright (c) 2011 Ryan Bates
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.rdoc CHANGED
@@ -1,6 +1,6 @@
1
1
  = CanCan
2
2
 
3
- Wiki[http://wiki.github.com/ryanb/cancan] | RDocs[http://rdoc.info/projects/ryanb/cancan] | Screencast[http://railscasts.com/episodes/192-authorization-with-cancan]
3
+ Wiki[https://github.com/ryanb/cancan/wiki] | RDocs[http://rdoc.info/projects/ryanb/cancan] | Screencast[http://railscasts.com/episodes/192-authorization-with-cancan]
4
4
 
5
5
  CanCan is an authorization library for Ruby on Rails which restricts what resources a given user is allowed to access. All permissions are defined in a single location (the +Ability+ class) and not duplicated across controllers, views, and database queries.
6
6
 
@@ -22,7 +22,7 @@ Alternatively, you can install it as a plugin.
22
22
 
23
23
  == Getting Started
24
24
 
25
- CanCan expects a +current_user+ method to exist. If you have not already, set up some authentication (such as Authlogic[http://github.com/binarylogic/authlogic] or Devise[http://github.com/plataformatec/devise]). See {Changing Defaults}[http://wiki.github.com/ryanb/cancan/changing-defaults] if you need different behavior.
25
+ CanCan expects a +current_user+ method to exist in controllers. If you have not already, set up some authentication (such as Authlogic[https://github.com/binarylogic/authlogic] or Devise[https://github.com/plataformatec/devise]). See {Changing Defaults}[https://github.com/ryanb/cancan/wiki/changing-defaults] if you need different behavior.
26
26
 
27
27
  Next create a class called +Ability+ in "models/ability.rb" or anywhere else in the load path. It should look similar to this.
28
28
 
@@ -38,15 +38,15 @@ Next create a class called +Ability+ in "models/ability.rb" or anywhere else in
38
38
  end
39
39
  end
40
40
 
41
- The +current_user+ is passed in to this method which is where the abilities are defined. See the "Defining Abilities" section below for more information.
41
+ The +current_user+ is passed in to this method which is where the abilities are defined. See {Defining Abilities}[https://github.com/ryanb/cancan/wiki/defining-abilities] for what can go here.
42
42
 
43
- The current user's permissions can be accessed using the "can?" and "cannot?" methods in the view and controller.
43
+ The current user's permissions can then be accessed using the "can?" and "cannot?" methods in the view and controller.
44
44
 
45
45
  <% if can? :update, @article %>
46
46
  <%= link_to "Edit", edit_article_path(@article) %>
47
47
  <% end %>
48
48
 
49
- See {Checking Abilities}[http://wiki.github.com/ryanb/cancan/checking-abilities] for more information
49
+ See {Checking Abilities}[https://github.com/ryanb/cancan/wiki/checking-abilities] for more information
50
50
 
51
51
  The "authorize!" method in the controller will raise an exception if the user is not able to perform the given action.
52
52
 
@@ -65,7 +65,7 @@ Setting this for every action can be tedious, therefore the +load_and_authorize_
65
65
  end
66
66
  end
67
67
 
68
- See {Authorizing Controller Actions}[http://wiki.github.com/ryanb/cancan/authorizing-controller-actions] for more information
68
+ See {Authorizing Controller Actions}[https://github.com/ryanb/cancan/wiki/authorizing-controller-actions] for more information
69
69
 
70
70
  If the user authorization fails, a <tt>CanCan::AccessDenied</tt> exception will be raised. You can catch this and modify its behavior in the +ApplicationController+.
71
71
 
@@ -76,87 +76,26 @@ If the user authorization fails, a <tt>CanCan::AccessDenied</tt> exception will
76
76
  end
77
77
  end
78
78
 
79
- See {Exception Handling}[http://wiki.github.com/ryanb/cancan/exception-handling] for more information.
80
-
81
-
82
- == Defining Abilities
83
-
84
- As shown above, the +Ability+ class is where all user permissions are defined. The current user model is passed into the initialize method so the permissions can be modified based on any user attributes. CanCan makes no assumption about how roles are handled in your application. See {Role Based Authorization}[http://wiki.github.com/ryanb/cancan/role-based-authorization] for an example.
85
-
86
- The +can+ method is used to define permissions and requires two arguments. The first one is the action you're setting the permission for, the second one is the class of object you're setting it on.
87
-
88
- can :update, Article
89
-
90
- You can pass an array for either of these parameters to match any one. In this case the user will have the ability to update or destroy both articles and comments.
91
-
92
- can [:update, :destroy], [Article, Comment]
93
-
94
- Use :+manage+ to represent any action and :+all+ to represent any class. Here are some examples.
95
-
96
- can :manage, Article # has permissions to do anything to articles
97
- can :read, :all # has permission to read any model
98
- can :manage, :all # has permission to do anything to any model
99
-
100
- You can pass a hash of conditions as the third argument to further define what the user is able to access. Here the user will only have permission to read active projects which he owns.
101
-
102
- can :read, Project, :active => true, :user_id => user.id
103
-
104
- See {Defining Abilities with Hashes}[http://wiki.github.com/ryanb/cancan/defining-abilities-with-hashes] for more information.
105
-
106
- Blocks can also be used if you need more control.
107
-
108
- can :update, Project do |project|
109
- project.groups.include?(user.group)
110
- end
111
-
112
- If the block returns true then the user has that ability for that project, otherwise he will be denied access. See {Defining Abilities with Blocks}[http://wiki.github.com/ryanb/cancan/defining-abilities-with-blocks] for more information.
113
-
114
-
115
- == Aliasing Actions
116
-
117
- You will usually be working with four actions when defining and checking permissions: :+read+, :+create+, :+update+, :+destroy+. These aren't the same as the 7 RESTful actions in Rails. CanCan automatically adds some default aliases for mapping those actions.
118
-
119
- alias_action :index, :show, :to => :read
120
- alias_action :new, :to => :create
121
- alias_action :edit, :to => :update
122
-
123
- Notice the +edit+ action is aliased to +update+. This means if the user is able to update a record he also has permission to edit it. You can define your own aliases in the +Ability+ class.
124
-
125
- alias_action :update, :destroy, :to => :modify
126
- can :modify, Comment
127
- can? :update, Comment # => true
128
-
129
- The +alias_action+ method is an instance method and usually called in +initialize+. See {Custom Actions}[http://wiki.github.com/ryanb/cancan/custom-actions] for information on adding other actions.
130
-
131
-
132
- == Fetching Records
133
-
134
- It is possible to fetch records which the user has permission to read using the +accessible_by+ scope in Active Record.
135
-
136
- @articles = Article.accessible_by(current_ability)
137
-
138
- Since version 1.4 this is done automatically when loading resources in the index action, so one rarely needs to do it manually.
139
-
140
- This will only work when abilities are defined using hash conditions, not blocks. See {Fetching Records}[http://wiki.github.com/ryanb/cancan/fetching-records] for more information.
79
+ See {Exception Handling}[https://github.com/ryanb/cancan/wiki/exception-handling] for more information.
141
80
 
142
81
 
143
82
  == Additional Docs
144
83
 
145
- * {Upgrading to 1.4}[http://github.com/ryanb/cancan/wiki/Upgrading-to-1.4]
146
- * {Nested Resources}[http://wiki.github.com/ryanb/cancan/nested-resources]
147
- * {Testing Abilities}[http://wiki.github.com/ryanb/cancan/testing-abilities]
148
- * {Accessing Request Data}[http://wiki.github.com/ryanb/cancan/accessing-request-data]
149
- * {Admin Namespace}[http://wiki.github.com/ryanb/cancan/admin-namespace]
150
- * {See more}[http://wiki.github.com/ryanb/cancan/]
84
+ * {Upgrading to 1.4}[https://github.com/ryanb/cancan/wiki/Upgrading-to-1.4]
85
+ * {Nested Resources}[https://github.com/ryanb/cancan/wiki/nested-resources]
86
+ * {Testing Abilities}[https://github.com/ryanb/cancan/wiki/testing-abilities]
87
+ * {Accessing Request Data}[https://github.com/ryanb/cancan/wiki/accessing-request-data]
88
+ * {Admin Namespace}[https://github.com/ryanb/cancan/wiki/admin-namespace]
89
+ * {See more}[https://github.com/ryanb/cancan/wiki]
151
90
 
152
91
 
153
92
  == Questions or Problems?
154
93
 
155
- If you have any issues with CanCan which you cannot find the solution to in the documentation, please add an {issue on GitHub}[http://github.com/ryanb/cancan/issues] or fork the project and send a pull request.
94
+ If you have any issues with CanCan which you cannot find the solution to in the documentation, please add an {issue on GitHub}[https://github.com/ryanb/cancan/issues] or fork the project and send a pull request.
156
95
 
157
- To get the specs running you should call +bundle+ and then +rake+. Specs currently do not work in Ruby 1.9 due to the RR mocking framework.
96
+ To get the specs running you should call +bundle+ and then +rake+. Specs currently do not work in Ruby 1.9 due to the RR mocking framework. See the {spec/README}[https://github.com/ryanb/cancan/blob/master/spec/README.rdoc] for more information.
158
97
 
159
98
 
160
99
  == Special Thanks
161
100
 
162
- CanCan was inspired by declarative_authorization[http://github.com/stffn/declarative_authorization/] and aegis[http://github.com/makandra/aegis]. Also many thanks to the CanCan contributors[http://github.com/ryanb/cancan/contributors]. See the CHANGELOG[http://github.com/ryanb/cancan/blob/master/CHANGELOG.rdoc] for the full list.
101
+ CanCan was inspired by declarative_authorization[https://github.com/stffn/declarative_authorization/] and aegis[https://github.com/makandra/aegis]. Also many thanks to the CanCan contributors[https://github.com/ryanb/cancan/contributors]. See the CHANGELOG[https://github.com/ryanb/cancan/blob/master/CHANGELOG.rdoc] for the full list.
data/Rakefile CHANGED
@@ -7,4 +7,12 @@ RSpec::Core::RakeTask.new do |t|
7
7
  t.verbose = false
8
8
  end
9
9
 
10
+ desc "Run specs for all adapters"
11
+ task :spec_all do
12
+ %w[active_record data_mapper mongoid].each do |model_adapter|
13
+ puts "MODEL_ADAPTER = #{model_adapter}"
14
+ system "rake spec MODEL_ADAPTER=#{model_adapter}"
15
+ end
16
+ end
17
+
10
18
  task :default => :spec
data/lib/cancan.rb CHANGED
@@ -1,8 +1,13 @@
1
1
  require 'cancan/ability'
2
- require 'cancan/can_definition'
2
+ require 'cancan/rule'
3
3
  require 'cancan/controller_resource'
4
4
  require 'cancan/controller_additions'
5
- require 'cancan/active_record_additions'
5
+ require 'cancan/model_additions'
6
6
  require 'cancan/exceptions'
7
- require 'cancan/query'
8
7
  require 'cancan/inherited_resource'
8
+
9
+ require 'cancan/model_adapters/abstract_adapter'
10
+ require 'cancan/model_adapters/default_adapter'
11
+ require 'cancan/model_adapters/active_record_adapter' if defined? ActiveRecord
12
+ require 'cancan/model_adapters/data_mapper_adapter' if defined? DataMapper
13
+ require 'cancan/model_adapters/mongoid_adapter' if defined? Mongoid
@@ -54,8 +54,8 @@ module CanCan
54
54
  #
55
55
  # Also see the RSpec Matchers to aid in testing.
56
56
  def can?(action, subject, *extra_args)
57
- match = relevant_can_definitions_for_match(action, subject).detect do |can_definition|
58
- can_definition.matches_conditions?(action, subject, extra_args)
57
+ match = relevant_rules_for_match(action, subject).detect do |rule|
58
+ rule.matches_conditions?(action, subject, extra_args)
59
59
  end
60
60
  match ? match.base_behavior : false
61
61
  end
@@ -122,7 +122,7 @@ module CanCan
122
122
  # end
123
123
  #
124
124
  def can(action = nil, subject = nil, conditions = nil, &block)
125
- can_definitions << CanDefinition.new(true, action, subject, conditions, block)
125
+ rules << Rule.new(true, action, subject, conditions, block)
126
126
  end
127
127
 
128
128
  # Defines an ability which cannot be done. Accepts the same arguments as "can".
@@ -138,7 +138,7 @@ module CanCan
138
138
  # end
139
139
  #
140
140
  def cannot(action = nil, subject = nil, conditions = nil, &block)
141
- can_definitions << CanDefinition.new(false, action, subject, conditions, block)
141
+ rules << Rule.new(false, action, subject, conditions, block)
142
142
  end
143
143
 
144
144
  # Alias one or more actions into another one.
@@ -186,11 +186,9 @@ module CanCan
186
186
  @aliased_actions = {}
187
187
  end
188
188
 
189
- # Returns a CanCan::Query instance to help generate database queries based on the ability.
190
- # If any relevant can definitions use a block then an exception will be raised because an
191
- # SQL query cannot be generated from blocks of code.
192
- def query(action, subject)
193
- Query.new(subject, relevant_can_definitions_for_query(action, subject))
189
+ def model_adapter(model_class, action)
190
+ adapter_class = ModelAdapters::AbstractAdapter.adapter_class(model_class)
191
+ adapter_class.new(model_class, relevant_rules_for_query(action, model_class))
194
192
  end
195
193
 
196
194
  # See ControllerAdditions#authorize! for documentation.
@@ -215,18 +213,18 @@ module CanCan
215
213
 
216
214
  def attributes_for(action, subject)
217
215
  attributes = {}
218
- relevant_can_definitions(action, subject).map do |can_definition|
219
- attributes.merge!(can_definition.attributes_from_conditions) if can_definition.base_behavior
216
+ relevant_rules(action, subject).map do |rule|
217
+ attributes.merge!(rule.attributes_from_conditions) if rule.base_behavior
220
218
  end
221
219
  attributes
222
220
  end
223
221
 
224
222
  def has_block?(action, subject)
225
- relevant_can_definitions(action, subject).any?(&:only_block?)
223
+ relevant_rules(action, subject).any?(&:only_block?)
226
224
  end
227
225
 
228
226
  def has_raw_sql?(action, subject)
229
- relevant_can_definitions(action, subject).any?(&:only_raw_sql?)
227
+ relevant_rules(action, subject).any?(&:only_raw_sql?)
230
228
  end
231
229
 
232
230
  private
@@ -259,30 +257,30 @@ module CanCan
259
257
  results
260
258
  end
261
259
 
262
- def can_definitions
263
- @can_definitions ||= []
260
+ def rules
261
+ @rules ||= []
264
262
  end
265
263
 
266
- # Returns an array of CanDefinition instances which match the action and subject
264
+ # Returns an array of Rule instances which match the action and subject
267
265
  # This does not take into consideration any hash conditions or block statements
268
- def relevant_can_definitions(action, subject)
269
- can_definitions.reverse.select do |can_definition|
270
- can_definition.expanded_actions = expand_actions(can_definition.actions)
271
- can_definition.relevant? action, subject
266
+ def relevant_rules(action, subject)
267
+ rules.reverse.select do |rule|
268
+ rule.expanded_actions = expand_actions(rule.actions)
269
+ rule.relevant? action, subject
272
270
  end
273
271
  end
274
272
 
275
- def relevant_can_definitions_for_match(action, subject)
276
- relevant_can_definitions(action, subject).each do |can_definition|
277
- if can_definition.only_raw_sql?
273
+ def relevant_rules_for_match(action, subject)
274
+ relevant_rules(action, subject).each do |rule|
275
+ if rule.only_raw_sql?
278
276
  raise Error, "The can? and cannot? call cannot be used with a raw sql 'can' definition. The checking code cannot be determined for #{action.inspect} #{subject.inspect}"
279
277
  end
280
278
  end
281
279
  end
282
280
 
283
- def relevant_can_definitions_for_query(action, subject)
284
- relevant_can_definitions(action, subject).each do |can_definition|
285
- if can_definition.only_block?
281
+ def relevant_rules_for_query(action, subject)
282
+ relevant_rules(action, subject).each do |rule|
283
+ if rule.only_block?
286
284
  raise Error, "The accessible_by call cannot be used with a block 'can' definition. The SQL cannot be determined for #{action.inspect} #{subject.inspect}"
287
285
  end
288
286
  end
@@ -166,6 +166,52 @@ module CanCan
166
166
  cancan_resource_class.add_before_filter(self, :authorize_resource, *args)
167
167
  end
168
168
 
169
+ # Skip both the loading and authorization behavior of CanCan for this given controller. This is primarily
170
+ # useful to skip the behavior of a superclass. You can pass :only and :except options to specify which actions
171
+ # to skip the effects on. It will apply to all actions by default.
172
+ #
173
+ # class ProjectsController < SomeOtherController
174
+ # skip_load_and_authorize_resource :only => :index
175
+ # end
176
+ #
177
+ # You can also pass the resource name as the first argument to skip that resource.
178
+ def skip_load_and_authorize_resource(*args)
179
+ skip_load_resource(*args)
180
+ skip_authorize_resource(*args)
181
+ end
182
+
183
+ # Skip both the loading behavior of CanCan. This is useful when using +load_and_authorize_resource+ but want to
184
+ # only do authorization on certain actions. You can pass :only and :except options to specify which actions to
185
+ # skip the effects on. It will apply to all actions by default.
186
+ #
187
+ # class ProjectsController < ApplicationController
188
+ # load_and_authorize_resource
189
+ # skip_load_resource :only => :index
190
+ # end
191
+ #
192
+ # You can also pass the resource name as the first argument to skip that resource.
193
+ def skip_load_resource(*args)
194
+ options = args.extract_options!
195
+ name = args.first
196
+ cancan_skipper[:load][name] = options
197
+ end
198
+
199
+ # Skip both the authorization behavior of CanCan. This is useful when using +load_and_authorize_resource+ but want to
200
+ # only do loading on certain actions. You can pass :only and :except options to specify which actions to
201
+ # skip the effects on. It will apply to all actions by default.
202
+ #
203
+ # class ProjectsController < ApplicationController
204
+ # load_and_authorize_resource
205
+ # skip_authorize_resource :only => :index
206
+ # end
207
+ #
208
+ # You can also pass the resource name as the first argument to skip that resource.
209
+ def skip_authorize_resource(*args)
210
+ options = args.extract_options!
211
+ name = args.first
212
+ cancan_skipper[:authorize][name] = options
213
+ end
214
+
169
215
  # Add this to a controller to ensure it performs authorization through +authorized+! or +authorize_resource+ call.
170
216
  # If neither of these authorization methods are called, a CanCan::AuthorizationNotPerformed exception will be raised.
171
217
  # This is normally added to the ApplicationController to ensure all controller actions do authorization.
@@ -209,6 +255,10 @@ module CanCan
209
255
  ControllerResource
210
256
  end
211
257
  end
258
+
259
+ def cancan_skipper
260
+ @_cancan_skipper ||= {:authorize => {}, :load => {}}
261
+ end
212
262
  end
213
263
 
214
264
  def self.included(base)
@@ -26,21 +26,38 @@ module CanCan
26
26
  end
27
27
 
28
28
  def load_resource
29
- if parent? || member_action?
30
- self.resource_instance ||= load_resource_instance
31
- elsif load_collection?
32
- self.collection_instance ||= load_collection
29
+ unless skip?(:load)
30
+ if load_instance?
31
+ self.resource_instance ||= load_resource_instance
32
+ elsif load_collection?
33
+ self.collection_instance ||= load_collection
34
+ end
33
35
  end
34
36
  end
35
37
 
36
38
  def authorize_resource
37
- @controller.authorize!(authorization_action, resource_instance || resource_class_with_parent)
39
+ unless skip?(:authorize)
40
+ @controller.authorize!(authorization_action, resource_instance || resource_class_with_parent)
41
+ end
38
42
  end
39
43
 
40
44
  def parent?
41
45
  @options.has_key?(:parent) ? @options[:parent] : @name && @name != name_from_controller.to_sym
42
46
  end
43
47
 
48
+ def skip?(behavior) # This could probably use some refactoring
49
+ options = @controller.class.cancan_skipper[behavior][@name]
50
+ if options.nil?
51
+ false
52
+ elsif options == {}
53
+ true
54
+ elsif options[:except] && ![options[:except]].flatten.include?(@params[:action].to_sym)
55
+ true
56
+ elsif [options[:only]].flatten.include?(@params[:action].to_sym)
57
+ true
58
+ end
59
+ end
60
+
44
61
  protected
45
62
 
46
63
  def load_resource_instance
@@ -51,9 +68,12 @@ module CanCan
51
68
  end
52
69
  end
53
70
 
71
+ def load_instance?
72
+ parent? || member_action?
73
+ end
74
+
54
75
  def load_collection?
55
- resource_base.respond_to?(:accessible_by) &&
56
- !current_ability.has_block?(authorization_action, resource_class)
76
+ resource_base.respond_to?(:accessible_by) && !current_ability.has_block?(authorization_action, resource_class)
57
77
  end
58
78
 
59
79
  def load_collection
@@ -61,20 +81,18 @@ module CanCan
61
81
  end
62
82
 
63
83
  def build_resource
64
- if @options[:singleton] && resource_base.respond_to?("build_#{name}")
65
- resource = resource_base.send("build_#{name}")
66
- else
67
- resource = resource_base.send("new")
68
- end
84
+ method_name = @options[:singleton] && resource_base.respond_to?("build_#{name}") ? "build_#{name}" : "new"
85
+ resource = resource_base.send(method_name, @params[name] || {})
69
86
  initial_attributes.each do |name, value|
70
87
  resource.send("#{name}=", value)
71
88
  end
72
- resource.attributes = @params[name] if @params[name]
73
89
  resource
74
90
  end
75
91
 
76
92
  def initial_attributes
77
- current_ability.attributes_for(@params[:action].to_sym, resource_class)
93
+ current_ability.attributes_for(@params[:action].to_sym, resource_class).delete_if do |key, value|
94
+ @params[name] && @params[name].include?(key)
95
+ end
78
96
  end
79
97
 
80
98
  def find_resource
@@ -118,7 +136,7 @@ module CanCan
118
136
  end
119
137
 
120
138
  def resource_instance
121
- @controller.instance_variable_get("@#{instance_name}")
139
+ @controller.instance_variable_get("@#{instance_name}") if load_instance?
122
140
  end
123
141
 
124
142
  def collection_instance=(instance)