actionpack 3.1.12 → 3.2.0.rc1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of actionpack might be problematic. Click here for more details.

Files changed (128) hide show
  1. data/CHANGELOG.md +5503 -108
  2. data/README.rdoc +3 -3
  3. data/lib/abstract_controller/asset_paths.rb +1 -1
  4. data/lib/abstract_controller/base.rb +1 -1
  5. data/lib/abstract_controller/callbacks.rb +102 -18
  6. data/lib/abstract_controller/helpers.rb +1 -1
  7. data/lib/abstract_controller/layouts.rb +116 -50
  8. data/lib/abstract_controller/logger.rb +1 -1
  9. data/lib/abstract_controller/railties/routes_helpers.rb +2 -2
  10. data/lib/abstract_controller/rendering.rb +1 -6
  11. data/lib/abstract_controller/view_paths.rb +6 -5
  12. data/lib/action_controller.rb +0 -15
  13. data/lib/action_controller/caching.rb +0 -1
  14. data/lib/action_controller/caching/actions.rb +5 -6
  15. data/lib/action_controller/caching/fragments.rb +18 -18
  16. data/lib/action_controller/caching/pages.rb +7 -6
  17. data/lib/action_controller/caching/sweeping.rb +1 -1
  18. data/lib/action_controller/log_subscriber.rb +8 -4
  19. data/lib/action_controller/metal.rb +7 -1
  20. data/lib/action_controller/metal/conditional_get.rb +49 -4
  21. data/lib/action_controller/metal/data_streaming.rb +17 -5
  22. data/lib/action_controller/metal/force_ssl.rb +8 -5
  23. data/lib/action_controller/metal/helpers.rb +7 -4
  24. data/lib/action_controller/metal/http_authentication.rb +9 -12
  25. data/lib/action_controller/metal/instrumentation.rb +9 -4
  26. data/lib/action_controller/metal/mime_responds.rb +4 -4
  27. data/lib/action_controller/metal/params_wrapper.rb +12 -8
  28. data/lib/action_controller/metal/redirecting.rb +7 -6
  29. data/lib/action_controller/metal/renderers.rb +9 -11
  30. data/lib/action_controller/metal/request_forgery_protection.rb +2 -1
  31. data/lib/action_controller/metal/rescue.rb +13 -0
  32. data/lib/action_controller/metal/responder.rb +11 -23
  33. data/lib/action_controller/metal/streaming.rb +0 -25
  34. data/lib/action_controller/railtie.rb +1 -0
  35. data/lib/action_controller/railties/paths.rb +4 -3
  36. data/lib/action_controller/record_identifier.rb +4 -4
  37. data/lib/action_controller/test_case.rb +60 -56
  38. data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +6 -6
  39. data/lib/action_dispatch.rb +5 -1
  40. data/lib/action_dispatch/http/cache.rb +27 -15
  41. data/lib/action_dispatch/http/filter_parameters.rb +3 -1
  42. data/lib/action_dispatch/http/headers.rb +3 -5
  43. data/lib/action_dispatch/http/mime_negotiation.rb +2 -1
  44. data/lib/action_dispatch/http/mime_type.rb +7 -3
  45. data/lib/action_dispatch/http/mime_types.rb +12 -0
  46. data/lib/action_dispatch/http/parameter_filter.rb +3 -1
  47. data/lib/action_dispatch/http/parameters.rb +0 -4
  48. data/lib/action_dispatch/http/request.rb +18 -68
  49. data/lib/action_dispatch/http/response.rb +11 -32
  50. data/lib/action_dispatch/http/upload.rb +3 -14
  51. data/lib/action_dispatch/http/url.rb +1 -1
  52. data/lib/action_dispatch/middleware/callbacks.rb +1 -2
  53. data/lib/action_dispatch/middleware/cookies.rb +20 -16
  54. data/lib/action_dispatch/middleware/debug_exceptions.rb +82 -0
  55. data/lib/action_dispatch/middleware/exception_wrapper.rb +78 -0
  56. data/lib/action_dispatch/middleware/flash.rb +6 -9
  57. data/lib/action_dispatch/middleware/params_parser.rb +6 -11
  58. data/lib/action_dispatch/middleware/public_exceptions.rb +30 -0
  59. data/lib/action_dispatch/middleware/reloader.rb +38 -14
  60. data/lib/action_dispatch/middleware/remote_ip.rb +66 -36
  61. data/lib/action_dispatch/middleware/request_id.rb +39 -0
  62. data/lib/action_dispatch/middleware/session/abstract_store.rb +4 -16
  63. data/lib/action_dispatch/middleware/session/cache_store.rb +50 -0
  64. data/lib/action_dispatch/middleware/session/cookie_store.rb +1 -1
  65. data/lib/action_dispatch/middleware/show_exceptions.rb +58 -142
  66. data/lib/action_dispatch/middleware/static.rb +2 -10
  67. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +1 -0
  68. data/lib/action_dispatch/middleware/templates/rescues/routing_error.erb +13 -8
  69. data/lib/action_dispatch/railtie.rb +15 -1
  70. data/lib/action_dispatch/routing.rb +1 -2
  71. data/lib/action_dispatch/routing/mapper.rb +108 -107
  72. data/lib/action_dispatch/routing/redirection.rb +63 -69
  73. data/lib/action_dispatch/routing/route_set.rb +75 -43
  74. data/lib/action_dispatch/routing/routes_proxy.rb +0 -4
  75. data/lib/action_dispatch/routing/url_for.rb +3 -3
  76. data/lib/action_dispatch/testing/assertions/response.rb +5 -7
  77. data/lib/action_dispatch/testing/assertions/routing.rb +10 -9
  78. data/lib/action_dispatch/testing/integration.rb +8 -25
  79. data/lib/action_dispatch/testing/test_process.rb +3 -2
  80. data/lib/action_dispatch/testing/test_request.rb +4 -23
  81. data/lib/action_pack/version.rb +3 -3
  82. data/lib/action_view.rb +1 -5
  83. data/lib/action_view/asset_paths.rb +7 -8
  84. data/lib/action_view/base.rb +7 -5
  85. data/lib/action_view/helpers/asset_paths.rb +1 -1
  86. data/lib/action_view/helpers/asset_tag_helper.rb +4 -8
  87. data/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb +3 -0
  88. data/lib/action_view/helpers/atom_feed_helper.rb +2 -2
  89. data/lib/action_view/helpers/capture_helper.rb +3 -3
  90. data/lib/action_view/helpers/controller_helper.rb +1 -1
  91. data/lib/action_view/helpers/date_helper.rb +26 -18
  92. data/lib/action_view/helpers/debug_helper.rb +1 -1
  93. data/lib/action_view/helpers/form_helper.rb +71 -13
  94. data/lib/action_view/helpers/form_options_helper.rb +65 -34
  95. data/lib/action_view/helpers/form_tag_helper.rb +24 -18
  96. data/lib/action_view/helpers/javascript_helper.rb +12 -3
  97. data/lib/action_view/helpers/number_helper.rb +3 -2
  98. data/lib/action_view/helpers/record_tag_helper.rb +51 -5
  99. data/lib/action_view/helpers/rendering_helper.rb +2 -2
  100. data/lib/action_view/helpers/sanitize_helper.rb +6 -7
  101. data/lib/action_view/helpers/tag_helper.rb +1 -1
  102. data/lib/action_view/helpers/text_helper.rb +5 -4
  103. data/lib/action_view/helpers/url_helper.rb +19 -11
  104. data/lib/action_view/locale/en.yml +6 -0
  105. data/lib/action_view/log_subscriber.rb +1 -1
  106. data/lib/action_view/lookup_context.rb +123 -125
  107. data/lib/action_view/path_set.rb +60 -13
  108. data/lib/action_view/renderer/abstract_renderer.rb +16 -11
  109. data/lib/action_view/renderer/partial_renderer.rb +59 -40
  110. data/lib/action_view/renderer/template_renderer.rb +29 -17
  111. data/lib/action_view/template.rb +0 -1
  112. data/lib/action_view/template/error.rb +6 -5
  113. data/lib/action_view/template/handlers.rb +0 -6
  114. data/lib/action_view/template/handlers/builder.rb +10 -1
  115. data/lib/action_view/template/handlers/erb.rb +2 -2
  116. data/lib/action_view/template/resolver.rb +20 -31
  117. data/lib/action_view/test_case.rb +7 -10
  118. data/lib/sprockets/assets.rake +1 -1
  119. data/lib/sprockets/bootstrap.rb +3 -31
  120. data/lib/sprockets/compressors.rb +69 -7
  121. data/lib/sprockets/helpers/rails_helper.rb +6 -11
  122. data/lib/sprockets/railtie.rb +1 -0
  123. data/lib/sprockets/static_compiler.rb +0 -3
  124. metadata +57 -86
  125. checksums.yaml +0 -7
  126. data/lib/action_dispatch/middleware/closed_error.rb +0 -7
  127. data/lib/action_dispatch/routing/route.rb +0 -67
  128. data/lib/action_view/template/handler.rb +0 -49
@@ -283,7 +283,7 @@ methods:
283
283
 
284
284
  The last two lines are responsible for telling ActionController where the
285
285
  template files are located and actually running the controller on a new
286
- request from the web-server (like to be Apache).
286
+ request from the web-server (e.g., Apache).
287
287
 
288
288
  And the templates look like this:
289
289
 
@@ -316,7 +316,7 @@ an URL such as /weblog/5 (where 5 is the id of the post).
316
316
 
317
317
  == Download and installation
318
318
 
319
- The latest version of Action Pack can be installed with Rubygems:
319
+ The latest version of Action Pack can be installed with RubyGems:
320
320
 
321
321
  % [sudo] gem install actionpack
322
322
 
@@ -334,7 +334,7 @@ Action Pack is released under the MIT license.
334
334
 
335
335
  API documentation is at
336
336
 
337
- * http://api.rubyonrails.com
337
+ * http://api.rubyonrails.org
338
338
 
339
339
  Bug reports and feature requests can be filed with the rest for the Ruby on Rails project here:
340
340
 
@@ -4,7 +4,7 @@ module AbstractController
4
4
 
5
5
  included do
6
6
  config_accessor :asset_host, :asset_path, :assets_dir, :javascripts_dir,
7
- :stylesheets_dir, :default_asset_host_protocol
7
+ :stylesheets_dir, :default_asset_host_protocol, :relative_url_root
8
8
  end
9
9
  end
10
10
  end
@@ -10,7 +10,7 @@ module AbstractController
10
10
  # <tt>AbstractController::Base</tt> is a low-level API. Nobody should be
11
11
  # using it directly, and subclasses (like ActionController::Base) are
12
12
  # expected to provide their own +render+ method, since rendering means
13
- # different things depending on the context.
13
+ # different things depending on the context.
14
14
  class Base
15
15
  attr_internal :response_body
16
16
  attr_internal :action_name
@@ -75,38 +75,122 @@ module AbstractController
75
75
  end
76
76
  end
77
77
 
78
+ ##
79
+ # :method: before_filter
80
+ #
81
+ # :call-seq: before_filter(names, block)
82
+ #
83
+ # Append a before filter. See _insert_callbacks for parameter details.
84
+
85
+ ##
86
+ # :method: prepend_before_filter
87
+ #
88
+ # :call-seq: prepend_before_filter(names, block)
89
+ #
90
+ # Prepend a before filter. See _insert_callbacks for parameter details.
91
+
92
+ ##
93
+ # :method: skip_before_filter
94
+ #
95
+ # :call-seq: skip_before_filter(names, block)
96
+ #
97
+ # Skip a before filter. See _insert_callbacks for parameter details.
98
+
99
+ ##
100
+ # :method: append_before_filter
101
+ #
102
+ # :call-seq: append_before_filter(names, block)
103
+ #
104
+ # Append a before filter. See _insert_callbacks for parameter details.
105
+
106
+ ##
107
+ # :method: after_filter
108
+ #
109
+ # :call-seq: after_filter(names, block)
110
+ #
111
+ # Append an after filter. See _insert_callbacks for parameter details.
112
+
113
+ ##
114
+ # :method: prepend_after_filter
115
+ #
116
+ # :call-seq: prepend_after_filter(names, block)
117
+ #
118
+ # Prepend an after filter. See _insert_callbacks for parameter details.
119
+
120
+ ##
121
+ # :method: skip_after_filter
122
+ #
123
+ # :call-seq: skip_after_filter(names, block)
124
+ #
125
+ # Skip an after filter. See _insert_callbacks for parameter details.
126
+
127
+ ##
128
+ # :method: append_after_filter
129
+ #
130
+ # :call-seq: append_after_filter(names, block)
131
+ #
132
+ # Append an after filter. See _insert_callbacks for parameter details.
133
+
134
+ ##
135
+ # :method: around_filter
136
+ #
137
+ # :call-seq: around_filter(names, block)
138
+ #
139
+ # Append an around filter. See _insert_callbacks for parameter details.
140
+
141
+ ##
142
+ # :method: prepend_around_filter
143
+ #
144
+ # :call-seq: prepend_around_filter(names, block)
145
+ #
146
+ # Prepend an around filter. See _insert_callbacks for parameter details.
147
+
148
+ ##
149
+ # :method: skip_around_filter
150
+ #
151
+ # :call-seq: skip_around_filter(names, block)
152
+ #
153
+ # Skip an around filter. See _insert_callbacks for parameter details.
154
+
155
+ ##
156
+ # :method: append_around_filter
157
+ #
158
+ # :call-seq: append_around_filter(names, block)
159
+ #
160
+ # Append an around filter. See _insert_callbacks for parameter details.
161
+
78
162
  # set up before_filter, prepend_before_filter, skip_before_filter, etc.
79
163
  # for each of before, after, and around.
80
164
  [:before, :after, :around].each do |filter|
81
165
  class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
82
166
  # Append a before, after or around filter. See _insert_callbacks
83
167
  # for details on the allowed parameters.
84
- def #{filter}_filter(*names, &blk)
85
- _insert_callbacks(names, blk) do |name, options|
86
- options[:if] = (Array.wrap(options[:if]) << "!halted") if #{filter == :after}
87
- set_callback(:process_action, :#{filter}, name, options)
88
- end
89
- end
168
+ def #{filter}_filter(*names, &blk) # def before_filter(*names, &blk)
169
+ _insert_callbacks(names, blk) do |name, options| # _insert_callbacks(names, blk) do |name, options|
170
+ options[:if] = (Array.wrap(options[:if]) << "!halted") if #{filter == :after} # options[:if] = (Array.wrap(options[:if]) << "!halted") if false
171
+ set_callback(:process_action, :#{filter}, name, options) # set_callback(:process_action, :before, name, options)
172
+ end # end
173
+ end # end
90
174
 
91
175
  # Prepend a before, after or around filter. See _insert_callbacks
92
176
  # for details on the allowed parameters.
93
- def prepend_#{filter}_filter(*names, &blk)
94
- _insert_callbacks(names, blk) do |name, options|
95
- options[:if] = (Array.wrap(options[:if]) << "!halted") if #{filter == :after}
96
- set_callback(:process_action, :#{filter}, name, options.merge(:prepend => true))
97
- end
98
- end
177
+ def prepend_#{filter}_filter(*names, &blk) # def prepend_before_filter(*names, &blk)
178
+ _insert_callbacks(names, blk) do |name, options| # _insert_callbacks(names, blk) do |name, options|
179
+ options[:if] = (Array.wrap(options[:if]) << "!halted") if #{filter == :after} # options[:if] = (Array.wrap(options[:if]) << "!halted") if false
180
+ set_callback(:process_action, :#{filter}, name, options.merge(:prepend => true)) # set_callback(:process_action, :before, name, options.merge(:prepend => true))
181
+ end # end
182
+ end # end
99
183
 
100
184
  # Skip a before, after or around filter. See _insert_callbacks
101
185
  # for details on the allowed parameters.
102
- def skip_#{filter}_filter(*names, &blk)
103
- _insert_callbacks(names, blk) do |name, options|
104
- skip_callback(:process_action, :#{filter}, name, options)
105
- end
106
- end
186
+ def skip_#{filter}_filter(*names, &blk) # def skip_before_filter(*names, &blk)
187
+ _insert_callbacks(names, blk) do |name, options| # _insert_callbacks(names, blk) do |name, options|
188
+ skip_callback(:process_action, :#{filter}, name, options) # skip_callback(:process_action, :before, name, options)
189
+ end # end
190
+ end # end
107
191
 
108
192
  # *_filter is the same as append_*_filter
109
- alias_method :append_#{filter}_filter, :#{filter}_filter
193
+ alias_method :append_#{filter}_filter, :#{filter}_filter # alias_method :append_before_filter, :before_filter
110
194
  RUBY_EVAL
111
195
  end
112
196
  end
@@ -67,7 +67,7 @@ module AbstractController
67
67
  # helper FooHelper # => includes FooHelper
68
68
  #
69
69
  # When the argument is a string or symbol, the method will provide the "_helper" suffix, require the file
70
- # and include the module in the template class. The second form illustrates how to include custom helpers
70
+ # and include the module in the template class. The second form illustrates how to include custom helpers
71
71
  # when working with namespaced controllers, or other cases where the file containing the helper definition is not
72
72
  # in one of Rails' standard load paths:
73
73
  # helper :foo # => requires 'foo_helper' and includes FooHelper
@@ -66,27 +66,40 @@ module AbstractController
66
66
  # == Inheritance Examples
67
67
  #
68
68
  # class BankController < ActionController::Base
69
- # layout "bank_standard"
69
+ # # bank.html.erb exists
70
+ #
71
+ # class ExchangeController < BankController
72
+ # # exchange.html.erb exists
73
+ #
74
+ # class CurrencyController < BankController
70
75
  #
71
76
  # class InformationController < BankController
77
+ # layout "information"
72
78
  #
73
- # class TellerController < BankController
79
+ # class TellerController < InformationController
74
80
  # # teller.html.erb exists
75
81
  #
76
- # class TillController < TellerController
82
+ # class EmployeeController < InformationController
83
+ # # employee.html.erb exists
84
+ # layout nil
77
85
  #
78
86
  # class VaultController < BankController
79
87
  # layout :access_level_layout
80
88
  #
81
- # class EmployeeController < BankController
82
- # layout nil
89
+ # class TillController < BankController
90
+ # layout false
91
+ #
92
+ # In these examples, we have three implicit lookup scenrios:
93
+ # * The BankController uses the "bank" layout.
94
+ # * The ExchangeController uses the "exchange" layout.
95
+ # * The CurrencyController inherits the layout from BankController.
83
96
  #
84
- # In these examples:
85
- # * The InformationController uses the "bank_standard" layout, inherited from BankController.
86
- # * The TellerController follows convention and uses +app/views/layouts/teller.html.erb+.
87
- # * The TillController inherits the layout from TellerController and uses +teller.html.erb+ as well.
97
+ # However, when a layout is explicitly set, the explicitly set layout wins:
98
+ # * The InformationController uses the "information" layout, explicitly set.
99
+ # * The TellerController also uses the "information" layout, because the parent explicitly set it.
100
+ # * The EmployeeController uses the "employee" layout, because it set the layout to nil, resetting the parent configuration.
88
101
  # * The VaultController chooses a layout dynamically by calling the <tt>access_level_layout</tt> method.
89
- # * The EmployeeController does not use a layout at all.
102
+ # * The TillController does not use a layout at all.
90
103
  #
91
104
  # == Types of layouts
92
105
  #
@@ -126,6 +139,22 @@ module AbstractController
126
139
  # If no directory is specified for the template name, the template will by default be looked for in <tt>app/views/layouts/</tt>.
127
140
  # Otherwise, it will be looked up relative to the template root.
128
141
  #
142
+ # Setting the layout to nil forces it to be looked up in the filesystem and fallbacks to the parent behavior if none exists.
143
+ # Setting it to nil is useful to re-enable template lookup overriding a previous configuration set in the parent:
144
+ #
145
+ # class ApplicationController < ActionController::Base
146
+ # layout "application"
147
+ # end
148
+ #
149
+ # class PostsController < ApplicationController
150
+ # # Will use "application" layout
151
+ # end
152
+ #
153
+ # class CommentsController < ApplicationController
154
+ # # Will search for "comments" layout and fallback "application" layout
155
+ # layout nil
156
+ # end
157
+ #
129
158
  # == Conditional layouts
130
159
  #
131
160
  # If you have a layout that by default is applied to all the actions of a controller, you still have the option of rendering
@@ -168,11 +197,12 @@ module AbstractController
168
197
  included do
169
198
  class_attribute :_layout_conditions
170
199
  remove_possible_method :_layout_conditions
171
- delegate :_layout_conditions, :to => :'self.class'
172
200
  self._layout_conditions = {}
173
201
  _write_layout_method
174
202
  end
175
203
 
204
+ delegate :_layout_conditions, :to => "self.class"
205
+
176
206
  module ClassMethods
177
207
  def inherited(klass)
178
208
  super
@@ -188,7 +218,7 @@ module AbstractController
188
218
  #
189
219
  # ==== Returns
190
220
  # * <tt> Boolean</tt> - True if the action has a layout, false otherwise.
191
- def action_has_layout?
221
+ def conditional_layout?
192
222
  return unless super
193
223
 
194
224
  conditions = _layout_conditions
@@ -244,43 +274,71 @@ module AbstractController
244
274
  def _write_layout_method
245
275
  remove_possible_method(:_layout)
246
276
 
247
- case defined?(@_layout) ? @_layout : nil
248
- when String
249
- self.class_eval %{def _layout; #{@_layout.inspect} end}, __FILE__, __LINE__
250
- when Symbol
251
- self.class_eval <<-ruby_eval, __FILE__, __LINE__ + 1
252
- def _layout
253
- #{@_layout}.tap do |layout|
254
- unless layout.is_a?(String) || !layout
255
- raise ArgumentError, "Your layout method :#{@_layout} returned \#{layout}. It " \
256
- "should have returned a String, false, or nil"
277
+ prefixes = _implied_layout_name =~ /\blayouts/ ? [] : ["layouts"]
278
+ name_clause = if name
279
+ <<-RUBY
280
+ lookup_context.find_all("#{_implied_layout_name}", #{prefixes.inspect}).first || super
281
+ RUBY
282
+ end
283
+
284
+ if defined?(@_layout)
285
+ layout_definition = case @_layout
286
+ when String
287
+ @_layout.inspect
288
+ when Symbol
289
+ <<-RUBY
290
+ #{@_layout}.tap do |layout|
291
+ unless layout.is_a?(String) || !layout
292
+ raise ArgumentError, "Your layout method :#{@_layout} returned \#{layout}. It " \
293
+ "should have returned a String, false, or nil"
294
+ end
257
295
  end
258
- end
296
+ RUBY
297
+ when Proc
298
+ define_method :_layout_from_proc, &@_layout
299
+ "_layout_from_proc(self)"
300
+ when false
301
+ nil
302
+ when true
303
+ raise ArgumentError, "Layouts must be specified as a String, Symbol, false, or nil"
304
+ when nil
305
+ name_clause
259
306
  end
260
- ruby_eval
261
- when Proc
262
- define_method :_layout_from_proc, &@_layout
263
- self.class_eval %{def _layout; _layout_from_proc(self) end}, __FILE__, __LINE__
264
- when false
265
- self.class_eval %{def _layout; end}, __FILE__, __LINE__
266
- when true
267
- raise ArgumentError, "Layouts must be specified as a String, Symbol, false, or nil"
268
- when nil
269
- if name
270
- _prefixes = _implied_layout_name =~ /\blayouts/ ? [] : ["layouts"]
307
+ else
308
+ # Add a deprecation if the parent layout was explicitly set and the child
309
+ # still does a dynamic lookup. In next Rails release, we should @_layout
310
+ # to be inheritable so we can skip the child lookup if the parent explicitly
311
+ # set the layout.
312
+ parent = self.superclass.instance_variable_get(:@_layout)
313
+ @_layout = nil
314
+ inspect = parent.is_a?(Proc) ? parent.inspect : parent
271
315
 
272
- self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
273
- def _layout
274
- if template_exists?("#{_implied_layout_name}", #{_prefixes.inspect})
275
- "#{_implied_layout_name}"
316
+ layout_definition = if parent.nil?
317
+ name_clause
318
+ elsif name
319
+ <<-RUBY
320
+ if template = lookup_context.find_all("#{_implied_layout_name}", #{prefixes.inspect}).first
321
+ ActiveSupport::Deprecation.warn 'Layout found at "#{_implied_layout_name}" for #{name} but parent controller ' \
322
+ 'set layout to #{inspect.inspect}. Please explicitly set your layout to "#{_implied_layout_name}" ' \
323
+ 'or set it to nil to force a dynamic lookup.'
324
+ template
276
325
  else
277
326
  super
278
327
  end
279
- end
280
- RUBY
281
- end
328
+ RUBY
329
+ end
282
330
  end
283
- self.class_eval { private :_layout }
331
+
332
+ self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
333
+ def _layout
334
+ if conditional_layout?
335
+ #{layout_definition}
336
+ else
337
+ #{name_clause}
338
+ end
339
+ end
340
+ private :_layout
341
+ RUBY
284
342
  end
285
343
  end
286
344
 
@@ -289,8 +347,7 @@ module AbstractController
289
347
 
290
348
  if _include_layout?(options)
291
349
  layout = options.key?(:layout) ? options.delete(:layout) : :default
292
- value = _layout_for_option(layout)
293
- options[:layout] = (value =~ /\blayouts/ ? value : "layouts/#{value}") if value
350
+ options[:layout] = _layout_for_option(layout)
294
351
  end
295
352
  end
296
353
 
@@ -305,6 +362,10 @@ module AbstractController
305
362
  @_action_has_layout
306
363
  end
307
364
 
365
+ def conditional_layout?
366
+ true
367
+ end
368
+
308
369
  private
309
370
 
310
371
  # This will be overwritten by _write_layout_method
@@ -316,9 +377,10 @@ module AbstractController
316
377
  # * <tt>name</tt> - The name of the template
317
378
  def _layout_for_option(name)
318
379
  case name
319
- when String then name
320
- when true then _default_layout(true)
321
- when :default then _default_layout(false)
380
+ when String then _normalize_layout(name)
381
+ when Proc then name
382
+ when true then Proc.new { _default_layout(true) }
383
+ when :default then Proc.new { _default_layout(false) }
322
384
  when false, nil then nil
323
385
  else
324
386
  raise ArgumentError,
@@ -326,6 +388,10 @@ module AbstractController
326
388
  end
327
389
  end
328
390
 
391
+ def _normalize_layout(value)
392
+ value.is_a?(String) && value !~ /\blayouts/ ? "layouts/#{value}" : value
393
+ end
394
+
329
395
  # Returns the default layout for this controller.
330
396
  # Optionally raises an exception if the layout could not be found.
331
397
  #
@@ -337,17 +403,17 @@ module AbstractController
337
403
  # * <tt>template</tt> - The template object for the default layout (or nil)
338
404
  def _default_layout(require_layout = false)
339
405
  begin
340
- layout_name = _layout if action_has_layout?
406
+ value = _layout if action_has_layout?
341
407
  rescue NameError => e
342
408
  raise e, "Could not render layout: #{e.message}"
343
409
  end
344
410
 
345
- if require_layout && action_has_layout? && !layout_name
411
+ if require_layout && action_has_layout? && !value
346
412
  raise ArgumentError,
347
413
  "There was no default layout for #{self.class} in #{view_paths.inspect}"
348
414
  end
349
415
 
350
- layout_name
416
+ _normalize_layout(value)
351
417
  end
352
418
 
353
419
  def _include_layout?(options)