cancancan 2.1.0 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,11 @@
1
+ require_relative 'controller_resource_loader.rb'
1
2
  module CanCan
2
3
  # Handle the load and authorization controller logic
3
4
  # so we don't clutter up all controllers with non-interface methods.
4
5
  # This class is used internally, so you do not need to call methods directly on it.
5
6
  class ControllerResource # :nodoc:
7
+ include ControllerResourceLoader
8
+
6
9
  def self.add_before_action(controller_class, method, *args)
7
10
  options = args.extract_options!
8
11
  resource_name = args.first
@@ -29,15 +32,6 @@ module CanCan
29
32
  authorize_resource
30
33
  end
31
34
 
32
- def load_resource
33
- return if skip?(:load)
34
- if load_instance?
35
- self.resource_instance ||= load_resource_instance
36
- elsif load_collection?
37
- self.collection_instance ||= load_collection
38
- end
39
- end
40
-
41
35
  def authorize_resource
42
36
  return if skip?(:authorize)
43
37
  @controller.authorize!(authorization_action, resource_instance || resource_class_with_parent)
@@ -56,11 +50,19 @@ module CanCan
56
50
 
57
51
  protected
58
52
 
59
- def load_resource_instance
60
- if !parent? && new_actions.include?(@params[:action].to_sym)
61
- build_resource
62
- elsif id_param || @options[:singleton]
63
- find_resource
53
+ # Returns the class used for this resource. This can be overriden by the :class option.
54
+ # If +false+ is passed in it will use the resource name as a symbol in which case it should
55
+ # only be used for authorization, not loading since there's no class to load through.
56
+ def resource_class
57
+ case @options[:class]
58
+ when false
59
+ name.to_sym
60
+ when nil
61
+ namespaced_name.to_s.camelize.constantize
62
+ when String
63
+ @options[:class].constantize
64
+ else
65
+ @options[:class]
64
66
  end
65
67
  end
66
68
 
@@ -72,95 +74,12 @@ module CanCan
72
74
  resource_base.respond_to?(:accessible_by) && !current_ability.has_block?(authorization_action, resource_class)
73
75
  end
74
76
 
75
- def load_collection
76
- resource_base.accessible_by(current_ability, authorization_action)
77
- end
78
-
79
- def build_resource
80
- resource = resource_base.new(resource_params || {})
81
- assign_attributes(resource)
82
- end
83
-
84
- def assign_attributes(resource)
85
- resource.send("#{parent_name}=", parent_resource) if @options[:singleton] && parent_resource
86
- initial_attributes.each do |attr_name, value|
87
- resource.send("#{attr_name}=", value)
88
- end
89
- resource
90
- end
91
-
92
- def initial_attributes
93
- current_ability.attributes_for(@params[:action].to_sym, resource_class).delete_if do |key, _value|
94
- resource_params && resource_params.include?(key)
95
- end
96
- end
97
-
98
- def find_resource
99
- if @options[:singleton] && parent_resource.respond_to?(name)
100
- parent_resource.send(name)
101
- elsif @options[:find_by]
102
- find_resource_using_find_by
103
- else
104
- adapter.find(resource_base, id_param)
105
- end
106
- end
107
-
108
- def find_resource_using_find_by
109
- if resource_base.respond_to? "find_by_#{@options[:find_by]}!"
110
- resource_base.send("find_by_#{@options[:find_by]}!", id_param)
111
- elsif resource_base.respond_to? 'find_by'
112
- resource_base.send('find_by', @options[:find_by].to_sym => id_param)
113
- else
114
- resource_base.send(@options[:find_by], id_param)
115
- end
116
- end
117
-
118
- def adapter
119
- ModelAdapters::AbstractAdapter.adapter_class(resource_class)
120
- end
121
-
122
- def authorization_action
123
- parent? ? parent_authorization_action : @params[:action].to_sym
124
- end
125
-
126
- def parent_authorization_action
127
- @options[:parent_action] || :show
128
- end
129
-
130
- def id_param
131
- @params[id_param_key].to_s if @params[id_param_key].present?
132
- end
133
-
134
- def id_param_key
135
- if @options[:id_param]
136
- @options[:id_param]
137
- else
138
- parent? ? :"#{name}_id" : :id
139
- end
140
- end
141
-
142
77
  def member_action?
143
78
  new_actions.include?(@params[:action].to_sym) || @options[:singleton] ||
144
79
  ((@params[:id] || @params[@options[:id_param]]) &&
145
80
  !collection_actions.include?(@params[:action].to_sym))
146
81
  end
147
82
 
148
- # Returns the class used for this resource. This can be overriden by the :class option.
149
- # If +false+ is passed in it will use the resource name as a symbol in which case it should
150
- # only be used for authorization, not loading since there's no class to load through.
151
- def resource_class
152
- case @options[:class]
153
- when false then
154
- name.to_sym
155
- when nil then
156
- namespaced_name.to_s.camelize.constantize
157
- when String then
158
- @options[:class].constantize
159
- else
160
- @options[:class]
161
- end
162
- end
163
-
164
83
  def resource_class_with_parent
165
84
  parent_resource ? { parent_resource => resource_class } : resource_class
166
85
  end
@@ -183,114 +102,10 @@ module CanCan
183
102
  @controller.instance_variable_get("@#{instance_name.to_s.pluralize}")
184
103
  end
185
104
 
186
- # The object that methods (such as "find", "new" or "build") are called on.
187
- # If the :through option is passed it will go through an association on that instance.
188
- # If the :shallow option is passed it will use the resource_class if there's no parent
189
- # If the :singleton option is passed it won't use the association because it needs to be handled later.
190
- def resource_base
191
- if @options[:through]
192
- resource_base_through
193
- else
194
- resource_class
195
- end
196
- end
197
-
198
- def resource_base_through
199
- if parent_resource
200
- if @options[:singleton]
201
- resource_class
202
- else
203
- parent_resource.send(@options[:through_association] || name.to_s.pluralize)
204
- end
205
- elsif @options[:shallow]
206
- resource_class
207
- else
208
- # maybe this should be a record not found error instead?
209
- raise AccessDenied.new(nil, authorization_action, resource_class)
210
- end
211
- end
212
-
213
- def parent_name
214
- @options[:through] && [@options[:through]].flatten.detect { |i| fetch_parent(i) }
215
- end
216
-
217
- # The object to load this resource through.
218
- def parent_resource
219
- parent_name && fetch_parent(parent_name)
220
- end
221
-
222
- def fetch_parent(name)
223
- if @controller.instance_variable_defined? "@#{name}"
224
- @controller.instance_variable_get("@#{name}")
225
- elsif @controller.respond_to?(name, true)
226
- @controller.send(name)
227
- end
228
- end
229
-
230
- def current_ability
231
- @controller.send(:current_ability)
232
- end
233
-
234
- def name
235
- @name || name_from_controller
236
- end
237
-
238
- def resource_params
239
- if parameters_require_sanitizing? && params_method.present?
240
- case params_method
241
- when Symbol then
242
- @controller.send(params_method)
243
- when String then
244
- @controller.instance_eval(params_method)
245
- when Proc then
246
- params_method.call(@controller)
247
- end
248
- else
249
- resource_params_by_namespaced_name
250
- end
251
- end
252
-
253
105
  def parameters_require_sanitizing?
254
106
  save_actions.include?(@params[:action].to_sym) || resource_params_by_namespaced_name.present?
255
107
  end
256
108
 
257
- def resource_params_by_namespaced_name
258
- if @options[:instance_name] && @params.key?(extract_key(@options[:instance_name]))
259
- @params[extract_key(@options[:instance_name])]
260
- elsif @options[:class] && @params.key?(extract_key(@options[:class]))
261
- @params[extract_key(@options[:class])]
262
- else
263
- params = @params[extract_key(namespaced_name)]
264
- params.is_a?(Hash) ? params : nil
265
- end
266
- end
267
-
268
- def params_method
269
- params_methods.each do |method|
270
- return method if (method.is_a?(Symbol) && @controller.respond_to?(method, true)) ||
271
- method.is_a?(String) || method.is_a?(Proc)
272
- end
273
- nil
274
- end
275
-
276
- def params_methods
277
- methods = ["#{@params[:action]}_params".to_sym, "#{name}_params".to_sym, :resource_params]
278
- methods.unshift(@options[:param_method]) if @options[:param_method].present?
279
- methods
280
- end
281
-
282
- def namespace
283
- @params[:controller].split('/')[0..-2]
284
- end
285
-
286
- def namespaced_name
287
- [namespace, name].join('/').singularize.camelize.safe_constantize || name
288
- end
289
-
290
- def name_from_controller
291
- @params[:controller].split('/').last.singularize
292
- end
293
-
294
109
  def instance_name
295
110
  @options[:instance_name] || name
296
111
  end
@@ -299,10 +114,6 @@ module CanCan
299
114
  [:index] + Array(@options[:collection])
300
115
  end
301
116
 
302
- def new_actions
303
- %i[new create] + Array(@options[:new])
304
- end
305
-
306
117
  def save_actions
307
118
  %i[create update]
308
119
  end
@@ -313,8 +124,12 @@ module CanCan
313
124
  Array(options).include?(@params[:action].to_sym)
314
125
  end
315
126
 
316
- def extract_key(value)
317
- value.to_s.underscore.tr('/', '_')
127
+ def adapter
128
+ ModelAdapters::AbstractAdapter.adapter_class(resource_class)
129
+ end
130
+
131
+ def current_ability
132
+ @controller.send(:current_ability)
318
133
  end
319
134
  end
320
135
  end
@@ -0,0 +1,24 @@
1
+ module CanCan
2
+ module ControllerResourceBuilder
3
+ protected
4
+
5
+ def build_resource
6
+ resource = resource_base.new(resource_params || {})
7
+ assign_attributes(resource)
8
+ end
9
+
10
+ def assign_attributes(resource)
11
+ resource.send("#{parent_name}=", parent_resource) if @options[:singleton] && parent_resource
12
+ initial_attributes.each do |attr_name, value|
13
+ resource.send("#{attr_name}=", value)
14
+ end
15
+ resource
16
+ end
17
+
18
+ def initial_attributes
19
+ current_ability.attributes_for(@params[:action].to_sym, resource_class).delete_if do |key, _value|
20
+ resource_params && resource_params.include?(key)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,35 @@
1
+ module CanCan
2
+ module ControllerResourceFinder
3
+ protected
4
+
5
+ def find_resource
6
+ if @options[:singleton] && parent_resource.respond_to?(name)
7
+ parent_resource.send(name)
8
+ elsif @options[:find_by]
9
+ find_resource_using_find_by
10
+ else
11
+ adapter.find(resource_base, id_param)
12
+ end
13
+ end
14
+
15
+ def find_resource_using_find_by
16
+ if resource_base.respond_to? 'find_by'
17
+ resource_base.send('find_by', @options[:find_by].to_sym => id_param)
18
+ else
19
+ resource_base.send(@options[:find_by], id_param)
20
+ end
21
+ end
22
+
23
+ def id_param
24
+ @params[id_param_key].to_s if @params[id_param_key].present?
25
+ end
26
+
27
+ def id_param_key
28
+ if @options[:id_param]
29
+ @options[:id_param]
30
+ else
31
+ parent? ? :"#{name}_id" : :id
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,116 @@
1
+ require_relative 'controller_resource_finder.rb'
2
+ require_relative 'controller_resource_name_finder.rb'
3
+ require_relative 'controller_resource_builder.rb'
4
+ require_relative 'controller_resource_sanitizer.rb'
5
+ module CanCan
6
+ module ControllerResourceLoader
7
+ include CanCan::ControllerResourceNameFinder
8
+ include CanCan::ControllerResourceFinder
9
+ include CanCan::ControllerResourceBuilder
10
+ include CanCan::ControllerResourceSanitizer
11
+
12
+ def load_resource
13
+ return if skip?(:load)
14
+ if load_instance?
15
+ self.resource_instance ||= load_resource_instance
16
+ elsif load_collection?
17
+ self.collection_instance ||= load_collection
18
+ end
19
+ end
20
+
21
+ protected
22
+
23
+ def new_actions
24
+ %i[new create] + Array(@options[:new])
25
+ end
26
+
27
+ def resource_params_by_key(key)
28
+ return unless @options[key] && @params.key?(extract_key(@options[key]))
29
+ @params[extract_key(@options[key])]
30
+ end
31
+
32
+ def resource_params_by_namespaced_name
33
+ resource_params_by_key(:instance_name) || resource_params_by_key(:class) || (
34
+ params = @params[extract_key(namespaced_name)]
35
+ params.respond_to?(:to_h) ? params : nil)
36
+ end
37
+
38
+ def resource_params
39
+ if parameters_require_sanitizing? && params_method.present?
40
+ sanitize_parameters
41
+ else
42
+ resource_params_by_namespaced_name
43
+ end
44
+ end
45
+
46
+ def fetch_parent(name)
47
+ if @controller.instance_variable_defined? "@#{name}"
48
+ @controller.instance_variable_get("@#{name}")
49
+ elsif @controller.respond_to?(name, true)
50
+ @controller.send(name)
51
+ end
52
+ end
53
+
54
+ # The object to load this resource through.
55
+ def parent_resource
56
+ parent_name && fetch_parent(parent_name)
57
+ end
58
+
59
+ def parent_name
60
+ @options[:through] && [@options[:through]].flatten.detect { |i| fetch_parent(i) }
61
+ end
62
+
63
+ def resource_base_through_parent_resource
64
+ if @options[:singleton]
65
+ resource_class
66
+ else
67
+ parent_resource.send(@options[:through_association] || name.to_s.pluralize)
68
+ end
69
+ end
70
+
71
+ def resource_base_through
72
+ if parent_resource
73
+ resource_base_through_parent_resource
74
+ elsif @options[:shallow]
75
+ resource_class
76
+ else
77
+ # maybe this should be a record not found error instead?
78
+ raise AccessDenied.new(nil, authorization_action, resource_class)
79
+ end
80
+ end
81
+
82
+ # The object that methods (such as "find", "new" or "build") are called on.
83
+ # If the :through option is passed it will go through an association on that instance.
84
+ # If the :shallow option is passed it will use the resource_class if there's no parent
85
+ # If the :singleton option is passed it won't use the association because it needs to be handled later.
86
+ def resource_base
87
+ @options[:through] ? resource_base_through : resource_class
88
+ end
89
+
90
+ def parent_authorization_action
91
+ @options[:parent_action] || :show
92
+ end
93
+
94
+ def authorization_action
95
+ parent? ? parent_authorization_action : @params[:action].to_sym
96
+ end
97
+
98
+ def load_collection
99
+ resource_base.accessible_by(current_ability, authorization_action)
100
+ end
101
+
102
+ def load_resource_instance
103
+ if !parent? && new_actions.include?(@params[:action].to_sym)
104
+ build_resource
105
+ elsif id_param || @options[:singleton]
106
+ find_resource
107
+ end
108
+ end
109
+
110
+ private
111
+
112
+ def extract_key(value)
113
+ value.to_s.underscore.tr('/', '_')
114
+ end
115
+ end
116
+ end