emmanuel-inherited_resources 0.9.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.
- data/CHANGELOG +95 -0
- data/MIT-LICENSE +20 -0
- data/README +528 -0
- data/Rakefile +37 -0
- data/VERSION +1 -0
- data/lib/inherited_resources/actions.rb +74 -0
- data/lib/inherited_resources/base.rb +42 -0
- data/lib/inherited_resources/base_helpers.rb +333 -0
- data/lib/inherited_resources/belongs_to_helpers.rb +89 -0
- data/lib/inherited_resources/class_methods.rb +334 -0
- data/lib/inherited_resources/dumb_responder.rb +20 -0
- data/lib/inherited_resources/has_scope_helpers.rb +65 -0
- data/lib/inherited_resources/legacy/respond_to.rb +151 -0
- data/lib/inherited_resources/legacy/responder.rb +181 -0
- data/lib/inherited_resources/polymorphic_helpers.rb +155 -0
- data/lib/inherited_resources/singleton_helpers.rb +95 -0
- data/lib/inherited_resources/url_helpers.rb +173 -0
- data/lib/inherited_resources.rb +23 -0
- data/test/aliases_test.rb +139 -0
- data/test/base_helpers_test.rb +76 -0
- data/test/base_test.rb +219 -0
- data/test/belongs_to_test.rb +87 -0
- data/test/class_methods_test.rb +137 -0
- data/test/customized_belongs_to_test.rb +76 -0
- data/test/defaults_test.rb +70 -0
- data/test/flash_test.rb +88 -0
- data/test/has_scope_test.rb +112 -0
- data/test/nested_belongs_to_test.rb +108 -0
- data/test/optional_belongs_to_test.rb +164 -0
- data/test/polymorphic_test.rb +186 -0
- data/test/redirect_to_test.rb +51 -0
- data/test/respond_to_test.rb +155 -0
- data/test/singleton_test.rb +83 -0
- data/test/test_helper.rb +30 -0
- data/test/url_helpers_test.rb +471 -0
- metadata +86 -0
@@ -0,0 +1,334 @@
|
|
1
|
+
module InheritedResources
|
2
|
+
module ClassMethods
|
3
|
+
|
4
|
+
protected
|
5
|
+
|
6
|
+
# Used to overwrite the default assumptions InheritedResources do. Whenever
|
7
|
+
# this method is called, it should be on the top of your controller, since
|
8
|
+
# almost other methods depends on the values given to <<tt>>defaults</tt>.
|
9
|
+
#
|
10
|
+
# == Options
|
11
|
+
#
|
12
|
+
# * <tt>:resource_class</tt> - The resource class which by default is guessed
|
13
|
+
# by the controller name. Defaults to Project in
|
14
|
+
# ProjectsController.
|
15
|
+
#
|
16
|
+
# * <tt>:collection_name</tt> - The name of the collection instance variable which
|
17
|
+
# is set on the index action. Defaults to :projects in
|
18
|
+
# ProjectsController.
|
19
|
+
#
|
20
|
+
# * <tt>:instance_name</tt> - The name of the singular instance variable which
|
21
|
+
# is set on all actions besides index action. Defaults to
|
22
|
+
# :project in ProjectsController.
|
23
|
+
#
|
24
|
+
# * <tt>:route_collection_name</tt> - The name of the collection route. Defaults to :collection_name.
|
25
|
+
#
|
26
|
+
# * <tt>:route_instance_name</tt> - The name of the singular route. Defaults to :instance_name.
|
27
|
+
#
|
28
|
+
# * <tt>:route_prefix</tt> - The route prefix which is automically set in namespaced
|
29
|
+
# controllers. Default to :admin on Admin::ProjectsController.
|
30
|
+
#
|
31
|
+
# * <tt>:singleton</tt> - Tells if this controller is singleton or not.
|
32
|
+
#
|
33
|
+
def defaults(options)
|
34
|
+
raise ArgumentError, 'Class method :defaults expects a hash of options.' unless options.is_a? Hash
|
35
|
+
|
36
|
+
options.symbolize_keys!
|
37
|
+
options.assert_valid_keys(:resource_class, :collection_name, :instance_name,
|
38
|
+
:class_name, :route_prefix, :route_collection_name,
|
39
|
+
:route_instance_name, :singleton)
|
40
|
+
|
41
|
+
self.resource_class = options.delete(:resource_class) if options.key?(:resource_class)
|
42
|
+
self.resource_class = options.delete(:class_name).constantize if options.key?(:class_name)
|
43
|
+
|
44
|
+
acts_as_singleton! if options.delete(:singleton)
|
45
|
+
|
46
|
+
config = self.resources_configuration[:self]
|
47
|
+
config[:route_prefix] = options.delete(:route_prefix) if options.key?(:route_prefix)
|
48
|
+
|
49
|
+
options.each do |key, value|
|
50
|
+
config[key] = value.to_sym
|
51
|
+
end
|
52
|
+
|
53
|
+
create_resources_url_helpers!
|
54
|
+
end
|
55
|
+
|
56
|
+
# Defines wich actions to keep from the inherited controller.
|
57
|
+
# Syntax is borrowed from resource_controller.
|
58
|
+
#
|
59
|
+
# actions :index, :show, :edit
|
60
|
+
# actions :all, :except => :index
|
61
|
+
#
|
62
|
+
def actions(*actions_to_keep)
|
63
|
+
raise ArgumentError, 'Wrong number of arguments. You have to provide which actions you want to keep.' if actions_to_keep.empty?
|
64
|
+
|
65
|
+
options = actions_to_keep.extract_options!
|
66
|
+
actions_to_keep.map!{ |a| a.to_s }
|
67
|
+
|
68
|
+
actions_to_remove = Array(options[:except])
|
69
|
+
actions_to_remove.map!{ |a| a.to_s }
|
70
|
+
|
71
|
+
actions_to_remove += ACTIONS.map{ |a| a.to_s } - actions_to_keep unless actions_to_keep.first == 'all'
|
72
|
+
actions_to_remove.uniq!
|
73
|
+
|
74
|
+
(instance_methods & actions_to_remove).each do |action|
|
75
|
+
undef_method action, "#{action}!"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Detects params from url and apply as scopes to your classes.
|
80
|
+
#
|
81
|
+
# Your model:
|
82
|
+
#
|
83
|
+
# class Graduation < ActiveRecord::Base
|
84
|
+
# named_scope :featured, :conditions => { :featured => true }
|
85
|
+
# named_scope :by_degree, proc {|degree| { :conditions => { :degree => degree } } }
|
86
|
+
# end
|
87
|
+
#
|
88
|
+
# Your controller:
|
89
|
+
#
|
90
|
+
# class GraduationsController < InheritedResources::Base
|
91
|
+
# has_scope :featured, :boolean => true, :only => :index
|
92
|
+
# has_scope :by_degree, :only => :index
|
93
|
+
# end
|
94
|
+
#
|
95
|
+
# Then for each request:
|
96
|
+
#
|
97
|
+
# /graduations
|
98
|
+
# #=> acts like a normal request
|
99
|
+
#
|
100
|
+
# /graduations?featured=true
|
101
|
+
# #=> calls the named scope and bring featured graduations
|
102
|
+
#
|
103
|
+
# /graduations?featured=true&by_degree=phd
|
104
|
+
# #=> brings featured graduations with phd degree
|
105
|
+
#
|
106
|
+
# You can retrieve the current scopes in use with <tt>current_scopes</tt>
|
107
|
+
# method. In the last case, it would return: { :featured => "true", :by_degree => "phd" }
|
108
|
+
#
|
109
|
+
# == Options
|
110
|
+
#
|
111
|
+
# * <tt>:boolean</tt> - When set to true, call the scope only when the param is true or 1,
|
112
|
+
# and does not send the value as argument.
|
113
|
+
#
|
114
|
+
# * <tt>:only</tt> - In which actions the scope is applied. By default is :all.
|
115
|
+
#
|
116
|
+
# * <tt>:except</tt> - In which actions the scope is not applied. By default is :none.
|
117
|
+
#
|
118
|
+
# * <tt>:as</tt> - The key in the params hash expected to find the scope.
|
119
|
+
# Defaults to the scope name.
|
120
|
+
#
|
121
|
+
# * <tt>:default</tt> - Default value for the scope. Whenever supplied the scope
|
122
|
+
# is always called. This is useful to add easy pagination.
|
123
|
+
#
|
124
|
+
def has_scope(*scopes)
|
125
|
+
options = scopes.extract_options!
|
126
|
+
|
127
|
+
options.symbolize_keys!
|
128
|
+
options.assert_valid_keys(:boolean, :key, :only, :except, :default, :as)
|
129
|
+
|
130
|
+
if options[:key]
|
131
|
+
ActiveSupport::Deprecation.warn "has_scope :key is deprecated, use :as instead"
|
132
|
+
options[:as] ||= options[:key]
|
133
|
+
end
|
134
|
+
|
135
|
+
if self.scopes_configuration.empty?
|
136
|
+
include HasScopeHelpers
|
137
|
+
helper_method :current_scopes
|
138
|
+
end
|
139
|
+
|
140
|
+
scopes.each do |scope|
|
141
|
+
self.scopes_configuration[scope] ||= {}
|
142
|
+
self.scopes_configuration[scope][:as] = options[:as] || scope
|
143
|
+
self.scopes_configuration[scope][:only] = Array(options[:only])
|
144
|
+
self.scopes_configuration[scope][:except] = Array(options[:except])
|
145
|
+
self.scopes_configuration[scope][:boolean] = options[:boolean] if options.key?(:boolean)
|
146
|
+
self.scopes_configuration[scope][:default] = options[:default] if options.key?(:default)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# Defines that this controller belongs to another resource.
|
151
|
+
#
|
152
|
+
# belongs_to :projects
|
153
|
+
#
|
154
|
+
# == Options
|
155
|
+
#
|
156
|
+
# * <tt>:parent_class</tt> - Allows you to specify what is the parent class.
|
157
|
+
#
|
158
|
+
# belongs_to :project, :parent_class => AdminProject
|
159
|
+
#
|
160
|
+
# * <tt>:class_name</tt> - Also allows you to specify the parent class, but you should
|
161
|
+
# give a string. Added for ActiveRecord belongs to compatibility.
|
162
|
+
#
|
163
|
+
# * <tt>:instance_name</tt> - The instance variable name. By default is the name of the association.
|
164
|
+
#
|
165
|
+
# belongs_to :project, :instance_name => :my_project
|
166
|
+
#
|
167
|
+
# * <tt>:finder</tt> - Specifies which method should be called to instantiate the parent.
|
168
|
+
#
|
169
|
+
# belongs_to :project, :finder => :find_by_title!
|
170
|
+
#
|
171
|
+
# This will make your projects be instantiated as:
|
172
|
+
#
|
173
|
+
# Project.find_by_title!(params[:project_id])
|
174
|
+
#
|
175
|
+
# Instead of:
|
176
|
+
#
|
177
|
+
# Project.find(params[:project_id])
|
178
|
+
#
|
179
|
+
# * <tt>:param</tt> - Allows you to specify params key to retrieve the id.
|
180
|
+
# Default is :association_id, which in this case is :project_id.
|
181
|
+
#
|
182
|
+
# * <tt>:route_name</tt> - Allows you to specify what is the route name in your url
|
183
|
+
# helper. By default is association name.
|
184
|
+
#
|
185
|
+
# * <tt>:collection_name</tt> - Tell how to retrieve the next collection. Let's
|
186
|
+
# suppose you have Tasks which belongs to Projects
|
187
|
+
# which belongs to companies. This will do somewhere
|
188
|
+
# down the road:
|
189
|
+
#
|
190
|
+
# @company.projects
|
191
|
+
#
|
192
|
+
# But if you want to retrieve instead:
|
193
|
+
#
|
194
|
+
# @company.admin_projects
|
195
|
+
#
|
196
|
+
# You supply the collection name.
|
197
|
+
#
|
198
|
+
# * <tt>:polymorphic</tt> - Tell the association is polymorphic.
|
199
|
+
#
|
200
|
+
# * <tt>:singleton</tt> - Tell it's a singleton association.
|
201
|
+
#
|
202
|
+
# * <tt>:optional</tt> - Tell the association is optional (it's a special
|
203
|
+
# type of polymorphic association)
|
204
|
+
#
|
205
|
+
def belongs_to(*symbols, &block)
|
206
|
+
options = symbols.extract_options!
|
207
|
+
|
208
|
+
options.symbolize_keys!
|
209
|
+
options.assert_valid_keys(:class_name, :parent_class, :instance_name, :param,
|
210
|
+
:finder, :route_name, :collection_name, :singleton,
|
211
|
+
:polymorphic, :optional)
|
212
|
+
|
213
|
+
optional = options.delete(:optional)
|
214
|
+
singleton = options.delete(:singleton)
|
215
|
+
polymorphic = options.delete(:polymorphic)
|
216
|
+
finder = options.delete(:finder)
|
217
|
+
|
218
|
+
include BelongsToHelpers if self.parents_symbols.empty?
|
219
|
+
|
220
|
+
acts_as_singleton! if singleton
|
221
|
+
acts_as_polymorphic! if polymorphic || optional
|
222
|
+
|
223
|
+
raise ArgumentError, 'You have to give me at least one association name.' if symbols.empty?
|
224
|
+
raise ArgumentError, 'You cannot define multiple associations with options: #{options.keys.inspect} to belongs to.' unless symbols.size == 1 || options.empty?
|
225
|
+
|
226
|
+
symbols.each do |symbol|
|
227
|
+
symbol = symbol.to_sym
|
228
|
+
|
229
|
+
if polymorphic || optional
|
230
|
+
self.parents_symbols << :polymorphic unless self.parents_symbols.include?(:polymorphic)
|
231
|
+
self.resources_configuration[:polymorphic][:symbols] << symbol
|
232
|
+
self.resources_configuration[:polymorphic][:optional] ||= optional
|
233
|
+
else
|
234
|
+
self.parents_symbols << symbol
|
235
|
+
end
|
236
|
+
|
237
|
+
config = self.resources_configuration[symbol] = {}
|
238
|
+
config[:parent_class] = options.delete(:parent_class)
|
239
|
+
config[:parent_class] ||= (options.delete(:class_name) || symbol).to_s.classify.constantize rescue nil
|
240
|
+
config[:collection_name] = options.delete(:collection_name) || symbol.to_s.pluralize.to_sym
|
241
|
+
config[:instance_name] = options.delete(:instance_name) || symbol
|
242
|
+
config[:param] = options.delete(:param) || :"#{symbol}_id"
|
243
|
+
config[:route_name] = options.delete(:route_name) || symbol
|
244
|
+
config[:finder] = finder || :find
|
245
|
+
end
|
246
|
+
|
247
|
+
if block_given?
|
248
|
+
class_eval(&block)
|
249
|
+
else
|
250
|
+
create_resources_url_helpers!
|
251
|
+
end
|
252
|
+
end
|
253
|
+
alias :nested_belongs_to :belongs_to
|
254
|
+
|
255
|
+
# A quick method to declare polymorphic belongs to.
|
256
|
+
#
|
257
|
+
def polymorphic_belongs_to(*symbols, &block)
|
258
|
+
options = symbols.extract_options!
|
259
|
+
options.merge!(:polymorphic => true)
|
260
|
+
belongs_to(*symbols << options, &block)
|
261
|
+
end
|
262
|
+
|
263
|
+
# A quick method to declare singleton belongs to.
|
264
|
+
#
|
265
|
+
def singleton_belongs_to(*symbols, &block)
|
266
|
+
options = symbols.extract_options!
|
267
|
+
options.merge!(:singleton => true)
|
268
|
+
belongs_to(*symbols << options, &block)
|
269
|
+
end
|
270
|
+
|
271
|
+
# A quick method to declare optional belongs to.
|
272
|
+
#
|
273
|
+
def optional_belongs_to(*symbols, &block)
|
274
|
+
options = symbols.extract_options!
|
275
|
+
options.merge!(:optional => true)
|
276
|
+
belongs_to(*symbols << options, &block)
|
277
|
+
end
|
278
|
+
|
279
|
+
private
|
280
|
+
|
281
|
+
def acts_as_singleton! #:nodoc:
|
282
|
+
unless self.resources_configuration[:self][:singleton]
|
283
|
+
self.resources_configuration[:self][:singleton] = true
|
284
|
+
include SingletonHelpers
|
285
|
+
actions :all, :except => :index
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
def acts_as_polymorphic! #:nodoc:
|
290
|
+
unless self.parents_symbols.include?(:polymorphic)
|
291
|
+
include PolymorphicHelpers
|
292
|
+
helper_method :parent, :parent_type, :parent_class, :parent?
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
# Initialize resources class accessors and set their default values.
|
297
|
+
#
|
298
|
+
def initialize_resources_class_accessors! #:nodoc:
|
299
|
+
# Initialize resource class
|
300
|
+
self.resource_class = begin
|
301
|
+
self.controller_name.classify.constantize
|
302
|
+
rescue NameError
|
303
|
+
nil
|
304
|
+
end
|
305
|
+
|
306
|
+
# Initialize resources configuration hash
|
307
|
+
self.resources_configuration ||= {}
|
308
|
+
config = self.resources_configuration[:self] = {}
|
309
|
+
config[:collection_name] = self.controller_name.to_sym
|
310
|
+
config[:instance_name] = self.controller_name.singularize.to_sym
|
311
|
+
|
312
|
+
config[:route_collection_name] = config[:collection_name]
|
313
|
+
config[:route_instance_name] = config[:instance_name]
|
314
|
+
|
315
|
+
# Deal with namespaced controllers
|
316
|
+
namespaces = self.controller_path.split('/')[0..-2]
|
317
|
+
config[:route_prefix] = namespaces.join('_') unless namespaces.empty?
|
318
|
+
|
319
|
+
# Initialize polymorphic, singleton, scopes and belongs_to parameters
|
320
|
+
self.parents_symbols ||= []
|
321
|
+
self.scopes_configuration ||= {}
|
322
|
+
self.resources_configuration[:polymorphic] ||= { :symbols => [], :optional => false }
|
323
|
+
end
|
324
|
+
|
325
|
+
# Hook called on inheritance.
|
326
|
+
#
|
327
|
+
def inherited(base) #:nodoc:
|
328
|
+
super(base)
|
329
|
+
base.send :initialize_resources_class_accessors!
|
330
|
+
base.send :create_resources_url_helpers!
|
331
|
+
end
|
332
|
+
|
333
|
+
end
|
334
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module InheritedResources
|
2
|
+
# = Dumb Responder
|
3
|
+
#
|
4
|
+
# This responder discards all messages sent to him.
|
5
|
+
#
|
6
|
+
class DumbResponder
|
7
|
+
|
8
|
+
instance_methods.each do |m|
|
9
|
+
undef_method m unless m =~ /^__/
|
10
|
+
end
|
11
|
+
|
12
|
+
# This is like a good husband, he will just listen everything that his wife
|
13
|
+
# says (which is a lot) without complaining. :)
|
14
|
+
#
|
15
|
+
def method_missing(*args)
|
16
|
+
nil
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module InheritedResources
|
2
|
+
|
3
|
+
# = has_scopes
|
4
|
+
#
|
5
|
+
# This module in included in your controller when has_scope is called for the
|
6
|
+
# first time.
|
7
|
+
#
|
8
|
+
module HasScopeHelpers
|
9
|
+
TRUE_VALUES = ["true", true, "1", 1] unless self.const_defined?(:TRUE_VALUES)
|
10
|
+
|
11
|
+
protected
|
12
|
+
|
13
|
+
# Overwrites apply to scope to implement default scope logic.
|
14
|
+
#
|
15
|
+
def apply_scope_to(target_object) #:nodoc:
|
16
|
+
@current_scopes ||= {}
|
17
|
+
|
18
|
+
self.scopes_configuration.each do |scope, options|
|
19
|
+
next unless apply_scope_to_action?(options)
|
20
|
+
key = options[:as]
|
21
|
+
|
22
|
+
if params.key?(key)
|
23
|
+
value, call_scope = params[key], true
|
24
|
+
elsif options.key?(:default)
|
25
|
+
value, call_scope = options[:default], true
|
26
|
+
value = value.call(self) if value.is_a?(Proc)
|
27
|
+
end
|
28
|
+
|
29
|
+
if call_scope
|
30
|
+
@current_scopes[key] = value
|
31
|
+
|
32
|
+
if options[:boolean]
|
33
|
+
target_object = target_object.send(scope) if TRUE_VALUES.include?(value)
|
34
|
+
else
|
35
|
+
target_object = target_object.send(scope, value)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
target_object
|
41
|
+
end
|
42
|
+
|
43
|
+
# Given an options with :only and :except arrays, check if the scope
|
44
|
+
# can be performed in the current action.
|
45
|
+
#
|
46
|
+
def apply_scope_to_action?(options) #:nodoc:
|
47
|
+
if options[:only].empty?
|
48
|
+
if options[:except].empty?
|
49
|
+
true
|
50
|
+
else
|
51
|
+
!options[:except].include?(action_name.to_sym)
|
52
|
+
end
|
53
|
+
else
|
54
|
+
options[:only].include?(action_name.to_sym)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Returns the scopes used in this action.
|
59
|
+
#
|
60
|
+
def current_scopes
|
61
|
+
@current_scopes || {}
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
module ActionController #:nodoc:
|
2
|
+
class Base #:nodoc:
|
3
|
+
attr_accessor :formats
|
4
|
+
|
5
|
+
# Defines mimes that are rendered by default when invoking respond_with.
|
6
|
+
#
|
7
|
+
# Examples:
|
8
|
+
#
|
9
|
+
# respond_to :html, :xml, :json
|
10
|
+
#
|
11
|
+
# All actions on your controller will respond to :html, :xml and :json.
|
12
|
+
#
|
13
|
+
# But if you want to specify it based on your actions, you can use only and
|
14
|
+
# except:
|
15
|
+
#
|
16
|
+
# respond_to :html
|
17
|
+
# respond_to :xml, :json, :except => [ :edit ]
|
18
|
+
#
|
19
|
+
# The definition above explicits that all actions respond to :html. And all
|
20
|
+
# actions except :edit respond to :xml and :json.
|
21
|
+
#
|
22
|
+
# You can specify also only parameters:
|
23
|
+
#
|
24
|
+
# respond_to :rjs, :only => :create
|
25
|
+
#
|
26
|
+
def self.respond_to(*mimes)
|
27
|
+
options = mimes.extract_options!
|
28
|
+
|
29
|
+
only_actions = Array(options.delete(:only))
|
30
|
+
except_actions = Array(options.delete(:except))
|
31
|
+
|
32
|
+
mimes.each do |mime|
|
33
|
+
mime = mime.to_sym
|
34
|
+
mimes_for_respond_to[mime] = {}
|
35
|
+
mimes_for_respond_to[mime][:only] = only_actions unless only_actions.empty?
|
36
|
+
mimes_for_respond_to[mime][:except] = except_actions unless except_actions.empty?
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Clear all mimes in respond_to.
|
41
|
+
#
|
42
|
+
def self.clear_respond_to
|
43
|
+
write_inheritable_attribute(:mimes_for_respond_to, ActiveSupport::OrderedHash.new)
|
44
|
+
end
|
45
|
+
|
46
|
+
class_inheritable_reader :mimes_for_respond_to
|
47
|
+
clear_respond_to
|
48
|
+
|
49
|
+
# If ApplicationController is already defined around here, we have to set
|
50
|
+
# mimes_for_respond_to hash as well.
|
51
|
+
#
|
52
|
+
ApplicationController.clear_respond_to if defined?(ApplicationController)
|
53
|
+
|
54
|
+
def respond_to(*mimes, &block)
|
55
|
+
raise ArgumentError, "respond_to takes either types or a block, never both" if mimes.any? && block_given?
|
56
|
+
|
57
|
+
responder = ActionController::MimeResponds::Responder.new(self)
|
58
|
+
mimes = collect_mimes_from_class_level if mimes.empty?
|
59
|
+
mimes.each { |mime| responder.send(mime) }
|
60
|
+
block.call(responder) if block_given?
|
61
|
+
|
62
|
+
if format = responder.negotiate_mime
|
63
|
+
self.response.template.template_format = format.to_sym
|
64
|
+
self.response.content_type = format.to_s
|
65
|
+
self.formats = [ format.to_sym ]
|
66
|
+
|
67
|
+
if response = responder.response_for(format)
|
68
|
+
response.call
|
69
|
+
else
|
70
|
+
default_render
|
71
|
+
end
|
72
|
+
else
|
73
|
+
head :not_acceptable
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def respond_with(*resources, &block)
|
78
|
+
respond_to(&block)
|
79
|
+
rescue ActionView::MissingTemplate
|
80
|
+
options = resources.extract_options!
|
81
|
+
(options.delete(:responder) || responder).call(self, resources, options)
|
82
|
+
end
|
83
|
+
|
84
|
+
def responder
|
85
|
+
ActionController::Responder
|
86
|
+
end
|
87
|
+
|
88
|
+
protected
|
89
|
+
|
90
|
+
# Collect mimes declared in the class method respond_to valid for the
|
91
|
+
# current action.
|
92
|
+
#
|
93
|
+
def collect_mimes_from_class_level #:nodoc:
|
94
|
+
action = action_name.to_sym
|
95
|
+
|
96
|
+
mimes_for_respond_to.keys.select do |mime|
|
97
|
+
config = mimes_for_respond_to[mime]
|
98
|
+
|
99
|
+
if config[:except]
|
100
|
+
!config[:except].include?(action)
|
101
|
+
elsif config[:only]
|
102
|
+
config[:only].include?(action)
|
103
|
+
else
|
104
|
+
true
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
module MimeResponds
|
111
|
+
class Responder #:nodoc:
|
112
|
+
attr_reader :order
|
113
|
+
|
114
|
+
def any(*args, &block)
|
115
|
+
if args.any?
|
116
|
+
args.each { |type| send(type, &block) }
|
117
|
+
else
|
118
|
+
custom(Mime::ALL, &block)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
alias :all :any
|
122
|
+
|
123
|
+
def custom(mime_type, &block)
|
124
|
+
mime_type = mime_type.is_a?(Mime::Type) ? mime_type : Mime::Type.lookup(mime_type.to_s)
|
125
|
+
@order << mime_type
|
126
|
+
@responses[mime_type] ||= block
|
127
|
+
end
|
128
|
+
|
129
|
+
def response_for(mime)
|
130
|
+
@responses[mime] || @responses[Mime::ALL]
|
131
|
+
end
|
132
|
+
|
133
|
+
def negotiate_mime
|
134
|
+
@mime_type_priority.each do |priority|
|
135
|
+
if priority == Mime::ALL
|
136
|
+
return @order.first
|
137
|
+
elsif @order.include?(priority)
|
138
|
+
return priority
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
if @order.include?(Mime::ALL)
|
143
|
+
return Mime::SET.first if @mime_type_priority.first == Mime::ALL
|
144
|
+
return @mime_type_priority.first
|
145
|
+
end
|
146
|
+
|
147
|
+
nil
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|