actionpack 3.0.0.beta → 3.0.0.beta2

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 (118) hide show
  1. data/CHANGELOG +291 -260
  2. data/lib/abstract_controller.rb +5 -2
  3. data/lib/abstract_controller/assigns.rb +21 -0
  4. data/lib/abstract_controller/base.rb +13 -5
  5. data/lib/abstract_controller/collector.rb +2 -0
  6. data/lib/abstract_controller/helpers.rb +4 -14
  7. data/lib/abstract_controller/layouts.rb +50 -99
  8. data/lib/abstract_controller/logger.rb +2 -2
  9. data/lib/abstract_controller/rendering.rb +105 -173
  10. data/lib/abstract_controller/view_paths.rb +69 -0
  11. data/lib/action_controller.rb +1 -2
  12. data/lib/action_controller/base.rb +10 -32
  13. data/lib/action_controller/caching.rb +19 -18
  14. data/lib/action_controller/caching/actions.rb +17 -11
  15. data/lib/action_controller/caching/fragments.rb +5 -17
  16. data/lib/action_controller/caching/pages.rb +24 -24
  17. data/lib/action_controller/caching/sweeping.rb +1 -3
  18. data/lib/action_controller/deprecated.rb +0 -2
  19. data/lib/action_controller/deprecated/base.rb +143 -0
  20. data/lib/action_controller/metal.rb +29 -26
  21. data/lib/action_controller/metal/compatibility.rb +18 -87
  22. data/lib/action_controller/metal/cookies.rb +0 -1
  23. data/lib/action_controller/metal/head.rb +1 -0
  24. data/lib/action_controller/metal/helpers.rb +2 -2
  25. data/lib/action_controller/metal/hide_actions.rb +4 -6
  26. data/lib/action_controller/metal/http_authentication.rb +18 -33
  27. data/lib/action_controller/metal/implicit_render.rb +21 -0
  28. data/lib/action_controller/metal/instrumentation.rb +1 -1
  29. data/lib/action_controller/metal/mime_responds.rb +2 -1
  30. data/lib/action_controller/metal/rack_delegation.rb +3 -8
  31. data/lib/action_controller/metal/redirecting.rb +2 -1
  32. data/lib/action_controller/metal/renderers.rb +4 -2
  33. data/lib/action_controller/metal/rendering.rb +31 -44
  34. data/lib/action_controller/metal/request_forgery_protection.rb +41 -4
  35. data/lib/action_controller/metal/responder.rb +2 -0
  36. data/lib/action_controller/metal/session_management.rb +0 -36
  37. data/lib/action_controller/metal/streaming.rb +20 -47
  38. data/lib/action_controller/metal/testing.rb +0 -1
  39. data/lib/action_controller/metal/url_for.rb +11 -148
  40. data/lib/action_controller/middleware.rb +2 -1
  41. data/lib/action_controller/polymorphic_routes.rb +1 -2
  42. data/lib/action_controller/railtie.rb +63 -10
  43. data/lib/action_controller/railties/{subscriber.rb → log_subscriber.rb} +5 -12
  44. data/lib/action_controller/railties/url_helpers.rb +14 -0
  45. data/lib/action_controller/record_identifier.rb +20 -1
  46. data/lib/action_controller/test_case.rb +123 -12
  47. data/lib/action_dispatch.rb +1 -0
  48. data/lib/action_dispatch/http/cache.rb +20 -3
  49. data/lib/action_dispatch/http/filter_parameters.rb +40 -25
  50. data/lib/action_dispatch/http/mime_negotiation.rb +6 -17
  51. data/lib/action_dispatch/http/mime_type.rb +2 -7
  52. data/lib/action_dispatch/http/request.rb +12 -33
  53. data/lib/action_dispatch/http/response.rb +35 -15
  54. data/lib/action_dispatch/http/upload.rb +2 -0
  55. data/lib/action_dispatch/http/url.rb +5 -32
  56. data/lib/action_dispatch/middleware/callbacks.rb +1 -1
  57. data/lib/action_dispatch/middleware/cookies.rb +4 -3
  58. data/lib/action_dispatch/middleware/params_parser.rb +4 -3
  59. data/lib/action_dispatch/middleware/remote_ip.rb +51 -0
  60. data/lib/action_dispatch/middleware/session/abstract_store.rb +1 -0
  61. data/lib/action_dispatch/middleware/session/cookie_store.rb +6 -8
  62. data/lib/action_dispatch/middleware/show_exceptions.rb +0 -14
  63. data/lib/action_dispatch/middleware/stack.rb +6 -2
  64. data/lib/action_dispatch/railtie.rb +3 -1
  65. data/lib/action_dispatch/routing.rb +2 -0
  66. data/lib/action_dispatch/routing/deprecated_mapper.rb +35 -7
  67. data/lib/action_dispatch/routing/mapper.rb +134 -48
  68. data/lib/action_dispatch/routing/route.rb +2 -2
  69. data/lib/action_dispatch/routing/route_set.rb +217 -158
  70. data/lib/action_dispatch/routing/url_for.rb +139 -0
  71. data/lib/action_dispatch/testing/assertions/response.rb +14 -61
  72. data/lib/action_dispatch/testing/assertions/routing.rb +25 -14
  73. data/lib/action_dispatch/testing/integration.rb +32 -50
  74. data/lib/action_dispatch/testing/performance_test.rb +3 -1
  75. data/lib/action_dispatch/testing/test_process.rb +2 -0
  76. data/lib/action_dispatch/testing/test_request.rb +2 -0
  77. data/lib/action_pack/version.rb +4 -3
  78. data/lib/action_view.rb +11 -6
  79. data/lib/action_view/base.rb +33 -121
  80. data/lib/action_view/context.rb +0 -2
  81. data/lib/action_view/helpers.rb +26 -23
  82. data/lib/action_view/helpers/active_model_helper.rb +28 -18
  83. data/lib/action_view/helpers/asset_tag_helper.rb +109 -54
  84. data/lib/action_view/helpers/atom_feed_helper.rb +2 -2
  85. data/lib/action_view/helpers/cache_helper.rb +22 -1
  86. data/lib/action_view/helpers/capture_helper.rb +22 -22
  87. data/lib/action_view/helpers/date_helper.rb +6 -5
  88. data/lib/action_view/helpers/form_helper.rb +78 -63
  89. data/lib/action_view/helpers/form_options_helper.rb +6 -4
  90. data/lib/action_view/helpers/form_tag_helper.rb +26 -15
  91. data/lib/action_view/helpers/javascript_helper.rb +90 -10
  92. data/lib/action_view/helpers/number_helper.rb +315 -118
  93. data/lib/action_view/helpers/prototype_helper.rb +19 -46
  94. data/lib/action_view/helpers/record_tag_helper.rb +4 -4
  95. data/lib/action_view/helpers/tag_helper.rb +7 -24
  96. data/lib/action_view/helpers/text_helper.rb +8 -7
  97. data/lib/action_view/helpers/translation_helper.rb +7 -5
  98. data/lib/action_view/helpers/url_helper.rb +19 -16
  99. data/lib/action_view/locale/en.yml +45 -6
  100. data/lib/action_view/lookup_context.rb +190 -0
  101. data/lib/action_view/paths.rb +22 -63
  102. data/lib/action_view/railtie.rb +14 -4
  103. data/lib/action_view/railties/{subscriber.rb → log_subscriber.rb} +1 -1
  104. data/lib/action_view/render/layouts.rb +73 -0
  105. data/lib/action_view/render/partials.rb +15 -41
  106. data/lib/action_view/render/rendering.rb +27 -78
  107. data/lib/action_view/template.rb +20 -24
  108. data/lib/action_view/template/error.rb +22 -2
  109. data/lib/action_view/template/handlers/erb.rb +33 -9
  110. data/lib/action_view/template/handlers/rjs.rb +1 -2
  111. data/lib/action_view/template/resolver.rb +46 -104
  112. data/lib/action_view/template/text.rb +5 -12
  113. data/lib/action_view/test_case.rb +14 -23
  114. metadata +83 -40
  115. data/lib/abstract_controller/compatibility.rb +0 -18
  116. data/lib/abstract_controller/localized_cache.rb +0 -49
  117. data/lib/action_controller/metal/configuration.rb +0 -28
  118. data/lib/action_controller/url_rewriter.rb +0 -76
@@ -3,20 +3,23 @@ $:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.inc
3
3
 
4
4
  require 'active_support/ruby/shim'
5
5
  require 'active_support/dependencies/autoload'
6
+ require 'active_support/core_ext/class/attribute'
6
7
  require 'active_support/core_ext/module/attr_internal'
7
8
  require 'active_support/core_ext/module/delegation'
9
+ require 'active_support/core_ext/module/anonymous'
10
+ require 'active_support/i18n'
8
11
 
9
12
  module AbstractController
10
13
  extend ActiveSupport::Autoload
11
14
 
15
+ autoload :Assigns
12
16
  autoload :Base
13
17
  autoload :Callbacks
14
18
  autoload :Collector
15
- autoload :Compatibility
16
19
  autoload :Helpers
17
20
  autoload :Layouts
18
- autoload :LocalizedCache
19
21
  autoload :Logger
20
22
  autoload :Rendering
21
23
  autoload :Translation
24
+ autoload :ViewPaths
22
25
  end
@@ -0,0 +1,21 @@
1
+ module AbstractController
2
+ module Assigns
3
+ # This method should return a hash with assigns.
4
+ # You can overwrite this configuration per controller.
5
+ # :api: public
6
+ def view_assigns
7
+ hash = {}
8
+ variables = instance_variable_names
9
+ variables -= protected_instance_variables if respond_to?(:protected_instance_variables)
10
+ variables.each { |name| hash[name] = instance_variable_get(name) }
11
+ hash
12
+ end
13
+
14
+ # This method assigns the hash specified in _assigns_hash to the given object.
15
+ # :api: private
16
+ # TODO Ideally, this should be done on AV::Base.new initialization.
17
+ def _evaluate_assigns(object)
18
+ view_assigns.each { |k,v| object.instance_variable_set(k, v) }
19
+ end
20
+ end
21
+ end
@@ -1,3 +1,5 @@
1
+ require 'active_support/ordered_options'
2
+
1
3
  module AbstractController
2
4
  class Error < StandardError; end
3
5
  class ActionNotFound < StandardError; end
@@ -5,7 +7,6 @@ module AbstractController
5
7
  class Base
6
8
  attr_internal :response_body
7
9
  attr_internal :action_name
8
- attr_internal :formats
9
10
 
10
11
  class << self
11
12
  attr_reader :abstract
@@ -28,6 +29,14 @@ module AbstractController
28
29
  @descendants ||= []
29
30
  end
30
31
 
32
+ def config
33
+ @config ||= ActiveSupport::InheritableOptions.new(superclass < Base ? superclass.config : {})
34
+ end
35
+
36
+ def configure
37
+ yield config
38
+ end
39
+
31
40
  # A list of all internal methods for a controller. This finds the first
32
41
  # abstract superclass of a controller, and gets a list of all public
33
42
  # instance methods on that abstract class. Public instance methods of
@@ -84,15 +93,14 @@ module AbstractController
84
93
  # ==== Returns
85
94
  # String
86
95
  def controller_path
87
- @controller_path ||= name && name.sub(/Controller$/, '').underscore
96
+ @controller_path ||= name.sub(/Controller$/, '').underscore unless anonymous?
88
97
  end
89
98
  end
90
99
 
91
100
  abstract!
92
101
 
93
- # Initialize controller with nil formats.
94
- def initialize #:nodoc:
95
- @_formats = nil
102
+ def config
103
+ @config ||= ActiveSupport::InheritableOptions.new(self.class.config)
96
104
  end
97
105
 
98
106
  # Calls the action going through the entire action dispatch stack.
@@ -1,3 +1,5 @@
1
+ require "action_dispatch/http/mime_type"
2
+
1
3
  module AbstractController
2
4
  module Collector
3
5
  def self.generate_method_for_mime(mime)
@@ -1,6 +1,4 @@
1
1
  require 'active_support/dependencies'
2
- require 'active_support/core_ext/class/attribute'
3
- require 'active_support/core_ext/module/delegation'
4
2
 
5
3
  module AbstractController
6
4
  module Helpers
@@ -8,16 +6,10 @@ module AbstractController
8
6
 
9
7
  include Rendering
10
8
 
11
- def self.next_serial
12
- @helper_serial ||= 0
13
- @helper_serial += 1
14
- end
15
-
16
9
  included do
17
- class_attribute :_helpers, :_helper_serial
10
+ class_attribute :_helpers
18
11
  delegate :_helpers, :to => :'self.class'
19
12
  self._helpers = Module.new
20
- self._helper_serial = ::AbstractController::Helpers.next_serial
21
13
  end
22
14
 
23
15
  module ClassMethods
@@ -27,7 +19,7 @@ module AbstractController
27
19
  def inherited(klass)
28
20
  helpers = _helpers
29
21
  klass._helpers = Module.new { include helpers }
30
- klass.class_eval { default_helper_module! unless name.blank? }
22
+ klass.class_eval { default_helper_module! unless anonymous? }
31
23
  super
32
24
  end
33
25
 
@@ -97,9 +89,7 @@ module AbstractController
97
89
  # helper(:three, BlindHelper) { def mice() 'mice' end }
98
90
  #
99
91
  def helper(*args, &block)
100
- self._helper_serial = AbstractController::Helpers.next_serial + 1
101
-
102
- _modules_for_helpers(args).each do |mod|
92
+ modules_for_helpers(args).each do |mod|
103
93
  add_template_helper(mod)
104
94
  end
105
95
 
@@ -134,7 +124,7 @@ module AbstractController
134
124
  # ==== Returns
135
125
  # Array[Module]:: A normalized list of modules for the list of
136
126
  # helpers provided.
137
- def _modules_for_helpers(args)
127
+ def modules_for_helpers(args)
138
128
  args.flatten.map! do |arg|
139
129
  case arg
140
130
  when String, Symbol
@@ -1,5 +1,4 @@
1
- require 'active_support/core_ext/class/attribute'
2
- require 'active_support/core_ext/module/delegation'
1
+ require "active_support/core_ext/module/remove_method"
3
2
 
4
3
  module AbstractController
5
4
  # Layouts reverse the common pattern of including shared headers and footers in many templates to isolate changes in
@@ -173,27 +172,7 @@ module AbstractController
173
172
  module ClassMethods
174
173
  def inherited(klass)
175
174
  super
176
- klass.class_eval do
177
- _write_layout_method
178
- @found_layouts = {}
179
- end
180
- end
181
-
182
- def clear_template_caches!
183
- @found_layouts.clear if defined? @found_layouts
184
- super
185
- end
186
-
187
- def cache_layout(details)
188
- layout = @found_layouts
189
- key = Thread.current[:format_locale_key]
190
-
191
- # Cache nil
192
- if layout.key?(key)
193
- return layout[key]
194
- else
195
- layout[key] = yield
196
- end
175
+ klass._write_layout_method
197
176
  end
198
177
 
199
178
  # This module is mixed in if layout conditions are provided. This means
@@ -205,7 +184,9 @@ module AbstractController
205
184
  #
206
185
  # ==== Returns
207
186
  # Boolean:: True if the action has a layout, false otherwise.
208
- def _action_has_layout?
187
+ def action_has_layout?
188
+ return unless super
189
+
209
190
  conditions = _layout_conditions
210
191
 
211
192
  if only = conditions[:only]
@@ -260,12 +241,14 @@ module AbstractController
260
241
  # name, return that string. Otherwise, use the superclass'
261
242
  # layout (which might also be implied)
262
243
  def _write_layout_method
244
+ remove_possible_method(:_layout)
245
+
263
246
  case defined?(@_layout) ? @_layout : nil
264
247
  when String
265
- self.class_eval %{def _layout(details) #{@_layout.inspect} end}
248
+ self.class_eval %{def _layout; #{@_layout.inspect} end}
266
249
  when Symbol
267
250
  self.class_eval <<-ruby_eval, __FILE__, __LINE__ + 1
268
- def _layout(details)
251
+ def _layout
269
252
  #{@_layout}.tap do |layout|
270
253
  unless layout.is_a?(String) || !layout
271
254
  raise ArgumentError, "Your layout method :#{@_layout} returned \#{layout}. It " \
@@ -276,21 +259,21 @@ module AbstractController
276
259
  ruby_eval
277
260
  when Proc
278
261
  define_method :_layout_from_proc, &@_layout
279
- self.class_eval %{def _layout(details) _layout_from_proc(self) end}
262
+ self.class_eval %{def _layout; _layout_from_proc(self) end}
280
263
  when false
281
- self.class_eval %{def _layout(details) end}
264
+ self.class_eval %{def _layout; end}
282
265
  when true
283
266
  raise ArgumentError, "Layouts must be specified as a String, Symbol, false, or nil"
284
267
  when nil
285
268
  if name
269
+ _prefix = "layouts" unless _implied_layout_name =~ /\blayouts/
270
+
286
271
  self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
287
- def _layout(details)
288
- self.class.cache_layout(details) do
289
- if template_exists?("#{_implied_layout_name}", details, :_prefix => "layouts")
290
- "#{_implied_layout_name}"
291
- else
292
- super
293
- end
272
+ def _layout
273
+ if template_exists?("#{_implied_layout_name}", #{_prefix.inspect})
274
+ "#{_implied_layout_name}"
275
+ else
276
+ super
294
277
  end
295
278
  end
296
279
  RUBY
@@ -300,42 +283,31 @@ module AbstractController
300
283
  end
301
284
  end
302
285
 
303
- def render_to_body(options = {})
304
- # In the case of a partial with a layout, handle the layout
305
- # here, and make sure the view does not try to handle it
306
- layout = options.delete(:layout) if options.key?(:partial)
307
-
308
- response = super
286
+ def _normalize_options(options)
287
+ super
309
288
 
310
- # This is a little bit messy. We need to explicitly handle partial
311
- # layouts here since the core lookup logic is in the view, but
312
- # we need to determine the layout based on the controller
313
- #
314
- # TODO: An easier way to handle this would probably be to override
315
- # render_template
316
- if layout
317
- layout = _layout_for_option(layout, options[:_template].details)
318
- response = layout.render(view_context, options[:locals] || {}) { response }
289
+ if _include_layout?(options)
290
+ layout = options.key?(:layout) ? options.delete(:layout) : :default
291
+ value = _layout_for_option(layout)
292
+ options[:layout] = (value =~ /\blayouts/ ? value : "layouts/#{value}") if value
319
293
  end
294
+ end
295
+
296
+ attr_writer :action_has_layout
297
+
298
+ def initialize(*)
299
+ @action_has_layout = true
300
+ super
301
+ end
320
302
 
321
- response
303
+ def action_has_layout?
304
+ @action_has_layout
322
305
  end
323
306
 
324
307
  private
325
308
 
326
309
  # This will be overwritten by _write_layout_method
327
- def _layout(details) end
328
-
329
- # Determine the layout for a given name and details.
330
- #
331
- # ==== Parameters
332
- # name<String>:: The name of the template
333
- # details<Hash{Symbol => Object}>:: A list of details to restrict
334
- # the lookup to. By default, layout lookup is limited to the
335
- # formats specified for the current request.
336
- def _layout_for_name(name, details)
337
- name && _find_layout(name, details)
338
- end
310
+ def _layout; end
339
311
 
340
312
  # Determine the layout for a given name and details, taking into account
341
313
  # the name type.
@@ -345,11 +317,11 @@ module AbstractController
345
317
  # details<Hash{Symbol => Object}>:: A list of details to restrict
346
318
  # the lookup to. By default, layout lookup is limited to the
347
319
  # formats specified for the current request.
348
- def _layout_for_option(name, details)
320
+ def _layout_for_option(name)
349
321
  case name
350
- when String then _layout_for_name(name, details)
351
- when true then _default_layout(details, true)
352
- when :default then _default_layout(details, false)
322
+ when String then name
323
+ when true then _default_layout(true)
324
+ when :default then _default_layout(false)
353
325
  when false, nil then nil
354
326
  else
355
327
  raise ArgumentError,
@@ -357,29 +329,6 @@ module AbstractController
357
329
  end
358
330
  end
359
331
 
360
- def _determine_template(options)
361
- super
362
-
363
- return unless (options.keys & [:text, :inline, :partial]).empty? || options.key?(:layout)
364
- layout = options.key?(:layout) ? options[:layout] : :default
365
- options[:_layout] = _layout_for_option(layout, options[:_template].details)
366
- end
367
-
368
- # Take in the name and details and find a Template.
369
- #
370
- # ==== Parameters
371
- # name<String>:: The name of the template to retrieve
372
- # details<Hash>:: A list of details to restrict the search by. This
373
- # might include details like the format or locale of the template.
374
- #
375
- # ==== Returns
376
- # Template:: A template object matching the name and details
377
- def _find_layout(name, details)
378
- # TODO: Make prefix actually part of details in ViewPath#find_by_parts
379
- prefix = details.key?(:prefix) ? details.delete(:prefix) : "layouts"
380
- find_template(name, details, :_prefix => prefix)
381
- end
382
-
383
332
  # Returns the default layout for this controller and a given set of details.
384
333
  # Optionally raises an exception if the layout could not be found.
385
334
  #
@@ -392,22 +341,24 @@ module AbstractController
392
341
  #
393
342
  # ==== Returns
394
343
  # Template:: The template object for the default layout (or nil)
395
- def _default_layout(details, require_layout = false)
396
- if require_layout && _action_has_layout? && !_layout(details)
397
- raise ArgumentError,
398
- "There was no default layout for #{self.class} in #{view_paths.inspect}"
399
- end
400
-
344
+ def _default_layout(require_layout = false)
401
345
  begin
402
- _layout_for_name(_layout(details), details) if _action_has_layout?
346
+ layout_name = _layout if action_has_layout?
403
347
  rescue NameError => e
404
348
  raise NoMethodError,
405
349
  "You specified #{@_layout.inspect} as the layout, but no such method was found"
406
350
  end
351
+
352
+ if require_layout && action_has_layout? && !layout_name
353
+ raise ArgumentError,
354
+ "There was no default layout for #{self.class} in #{view_paths.inspect}"
355
+ end
356
+
357
+ layout_name
407
358
  end
408
359
 
409
- def _action_has_layout?
410
- true
360
+ def _include_layout?(options)
361
+ (options.keys & [:text, :inline, :partial]).empty? || options.key?(:layout)
411
362
  end
412
363
  end
413
364
  end
@@ -1,5 +1,5 @@
1
- require 'active_support/core_ext/logger'
2
- require 'active_support/benchmarkable'
1
+ require "active_support/core_ext/logger"
2
+ require "active_support/benchmarkable"
3
3
 
4
4
  module AbstractController
5
5
  module Logger
@@ -1,7 +1,4 @@
1
1
  require "abstract_controller/base"
2
- require 'active_support/core_ext/class/attribute'
3
- require 'active_support/core_ext/module/delegation'
4
- require 'active_support/core_ext/array/wrap'
5
2
 
6
3
  module AbstractController
7
4
  class DoubleRenderError < Error
@@ -12,90 +9,110 @@ module AbstractController
12
9
  end
13
10
  end
14
11
 
12
+ # This is a class to fix I18n global state. Whenever you provide I18n.locale during a request,
13
+ # it will trigger the lookup_context and consequently expire the cache.
14
+ # TODO Add some deprecation warnings to remove I18n.locale from controllers
15
+ class I18nProxy < ::I18n::Config #:nodoc:
16
+ attr_reader :i18n_config, :lookup_context
17
+
18
+ def initialize(i18n_config, lookup_context)
19
+ @i18n_config, @lookup_context = i18n_config, lookup_context
20
+ end
21
+
22
+ def locale
23
+ @i18n_config.locale
24
+ end
25
+
26
+ def locale=(value)
27
+ @i18n_config.locale = value
28
+ @lookup_context.update_details(:locale => @i18n_config.locale)
29
+ end
30
+ end
31
+
15
32
  module Rendering
16
33
  extend ActiveSupport::Concern
17
34
 
18
- included do
19
- class_attribute :_view_paths
20
- delegate :_view_paths, :to => :'self.class'
21
- self._view_paths = ActionView::PathSet.new
35
+ include AbstractController::ViewPaths
36
+
37
+ # Overwrite process to setup I18n proxy.
38
+ def process(*) #:nodoc:
39
+ old_config, I18n.config = I18n.config, I18nProxy.new(I18n.config, lookup_context)
40
+ super
41
+ ensure
42
+ I18n.config = old_config
43
+ end
44
+
45
+ module ClassMethods
46
+ def view_context_class
47
+ @view_context_class ||= begin
48
+ controller = self
49
+ Class.new(ActionView::Base) do
50
+ if controller.respond_to?(:_helpers)
51
+ include controller._helpers
52
+
53
+ if controller.respond_to?(:_router)
54
+ include controller._router.url_helpers
55
+ end
56
+
57
+ # TODO: Fix RJS to not require this
58
+ self.helpers = controller._helpers
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ attr_writer :view_context_class
66
+
67
+ def view_context_class
68
+ @view_context_class || self.class.view_context_class
69
+ end
70
+
71
+ def initialize(*)
72
+ @view_context_class = nil
73
+ super
22
74
  end
23
75
 
24
76
  # An instance of a view class. The default view class is ActionView::Base
25
77
  #
26
78
  # The view class must have the following methods:
27
- # View.for_controller[controller] Create a new ActionView instance for a
28
- # controller
29
- # View#render_partial[options]
30
- # - responsible for setting options[:_template]
31
- # - Returns String with the rendered partial
32
- # options<Hash>:: see _render_partial in ActionView::Base
33
- # View#render_template[template, layout, options, partial]
34
- # - Returns String with the rendered template
35
- # template<ActionView::Template>:: The template to render
36
- # layout<ActionView::Template>:: The layout to render around the template
37
- # options<Hash>:: See _render_template_with_layout in ActionView::Base
38
- # partial<Boolean>:: Whether or not the template to render is a partial
79
+ # View.new[lookup_context, assigns, controller]
80
+ # Create a new ActionView instance for a controller
81
+ # View#render[options]
82
+ # Returns String with the rendered template
39
83
  #
40
- # Override this method in a to change the default behavior.
84
+ # Override this method in a module to change the default behavior.
41
85
  def view_context
42
- @_view_context ||= ActionView::Base.for_controller(self)
86
+ view_context_class.new(lookup_context, view_assigns, self)
43
87
  end
44
88
 
45
- # Mostly abstracts the fact that calling render twice is a DoubleRenderError.
46
- # Delegates render_to_body and sticks the result in self.response_body.
89
+ # Normalize arguments, options and then delegates render_to_body and
90
+ # sticks the result in self.response_body.
47
91
  def render(*args, &block)
48
- options = _normalize_options(*args, &block)
92
+ options = _normalize_args(*args, &block)
93
+ _normalize_options(options)
49
94
  self.response_body = render_to_body(options)
50
95
  end
51
96
 
52
97
  # Raw rendering of a template to a Rack-compatible body.
53
- #
54
- # ==== Options
55
- # _partial_object<Object>:: The object that is being rendered. If this
56
- # exists, we are in the special case of rendering an object as a partial.
57
- #
58
98
  # :api: plugin
59
99
  def render_to_body(options = {})
60
- # TODO: Refactor so we can just use the normal template logic for this
61
- if options.key?(:partial)
62
- _render_partial(options)
63
- else
64
- _determine_template(options)
65
- _render_template(options)
66
- end
100
+ _process_options(options)
101
+ _render_template(options)
67
102
  end
68
103
 
69
104
  # Raw rendering of a template to a string. Just convert the results of
70
105
  # render_to_body into a String.
71
- #
72
106
  # :api: plugin
73
- def render_to_string(*args)
74
- options = _normalize_options(*args)
75
- AbstractController::Rendering.body_to_s(render_to_body(options))
76
- end
77
-
78
- # Renders the template from an object.
79
- #
80
- # ==== Options
81
- # _template<ActionView::Template>:: The template to render
82
- # _layout<ActionView::Template>:: The layout to wrap the template in (optional)
83
- def _render_template(options)
84
- view_context.render_template(options)
85
- end
86
-
87
- # Renders the given partial.
88
- #
89
- # ==== Options
90
- # partial<String|Object>:: The partial name or the object to be rendered
91
- def _render_partial(options)
92
- view_context.render_partial(options)
107
+ def render_to_string(options={})
108
+ _normalize_options(options)
109
+ render_to_body(options)
93
110
  end
94
111
 
95
- # The list of view paths for this controller. See ActionView::ViewPathSet for
96
- # more details about writing custom view paths.
97
- def view_paths
98
- _view_paths
112
+ # Find and renders a template based on the options given.
113
+ # :api: private
114
+ def _render_template(options) #:nodoc:
115
+ view_context.render(options)
99
116
  end
100
117
 
101
118
  # The prefix used in render "foo" shortcuts.
@@ -103,136 +120,51 @@ module AbstractController
103
120
  controller_path
104
121
  end
105
122
 
106
- # Return a string representation of a Rack-compatible response body.
107
- def self.body_to_s(body)
108
- if body.respond_to?(:to_str)
109
- body
110
- else
111
- strings = []
112
- body.each { |part| strings << part.to_s }
113
- body.close if body.respond_to?(:close)
114
- strings.join
115
- end
116
- end
117
-
118
123
  private
119
124
 
120
- # Normalize options, by converting render "foo" to render :template => "prefix/foo"
121
- # and render "/foo" to render :file => "/foo".
122
- def _normalize_options(action=nil, options={})
125
+ # This method should return a hash with assigns.
126
+ # You can overwrite this configuration per controller.
127
+ # :api: public
128
+ def view_assigns
129
+ hash = {}
130
+ variables = instance_variable_names
131
+ variables -= protected_instance_variables if respond_to?(:protected_instance_variables)
132
+ variables.each { |name| hash[name.to_s[1..-1]] = instance_variable_get(name) }
133
+ hash
134
+ end
135
+
136
+ # Normalize options by converting render "foo" to render :action => "foo" and
137
+ # render "foo/bar" to render :file => "foo/bar".
138
+ def _normalize_args(action=nil, options={})
123
139
  case action
140
+ when NilClass
124
141
  when Hash
125
142
  options, action = action, nil
126
143
  when String, Symbol
127
144
  action = action.to_s
128
- case action.index("/")
129
- when NilClass
130
- options[:_prefix] = _prefix
131
- options[:_template_name] = action
132
- when 0
133
- options[:file] = action
134
- else
135
- options[:template] = action
136
- end
145
+ key = action.include?(?/) ? :file : :action
146
+ options[key] = action
147
+ else
148
+ options.merge!(:partial => action)
137
149
  end
138
150
 
139
151
  options
140
152
  end
141
153
 
142
- # Take in a set of options and determine the template to render
143
- #
144
- # ==== Options
145
- # _template<ActionView::Template>:: If this is provided, the search is over
146
- # _template_name<#to_s>:: The name of the template to look up. Otherwise,
147
- # use the current action name.
148
- # _prefix<String>:: The prefix to look inside of. In a file system, this corresponds
149
- # to a directory.
150
- # _partial<TrueClass, FalseClass>:: Whether or not the file to look up is a partial
151
- def _determine_template(options)
152
- if options.key?(:text)
153
- options[:_template] = ActionView::Template::Text.new(options[:text], format_for_text)
154
- elsif options.key?(:inline)
155
- handler = ActionView::Template.handler_class_for_extension(options[:type] || "erb")
156
- template = ActionView::Template.new(options[:inline], "inline template", handler, {})
157
- options[:_template] = template
158
- elsif options.key?(:template)
159
- options[:_template_name] = options[:template]
160
- elsif options.key?(:file)
161
- options[:_template_name] = options[:file]
154
+ def _normalize_options(options)
155
+ if options[:partial] == true
156
+ options[:partial] = action_name
162
157
  end
163
- name = (options[:_template_name] || options[:action] || action_name).to_s
164
- options[:_prefix] ||= _prefix if (options.keys & [:partial, :file, :template]).empty?
165
-
166
- details = _normalize_details(options)
167
158
 
168
- options[:_template] ||= with_template_cache(name) do
169
- find_template(name, details, options)
159
+ if (options.keys & [:partial, :file, :template]).empty?
160
+ options[:prefix] ||= _prefix
170
161
  end
171
- end
172
-
173
- def _normalize_details(options)
174
- details = { :formats => formats }
175
- details[:formats] = Array(options[:format]) if options[:format]
176
- details[:locale] = Array(options[:locale]) if options[:locale]
177
- details
178
- end
179
-
180
- def find_template(name, details, options)
181
- view_paths.find(name, details, options[:_prefix], options[:_partial])
182
- end
183
162
 
184
- def template_exists?(name, details, options)
185
- view_paths.exists?(name, details, options[:_prefix], options[:_partial])
186
- end
187
-
188
- def with_template_cache(name)
189
- yield
190
- end
191
-
192
- def format_for_text
193
- Mime[:text]
163
+ options[:template] ||= (options[:action] || action_name).to_s
164
+ options
194
165
  end
195
166
 
196
- module ClassMethods
197
- def clear_template_caches!
198
- end
199
-
200
- # Append a path to the list of view paths for this controller.
201
- #
202
- # ==== Parameters
203
- # path<String, ViewPath>:: If a String is provided, it gets converted into
204
- # the default view path. You may also provide a custom view path
205
- # (see ActionView::ViewPathSet for more information)
206
- def append_view_path(path)
207
- self.view_paths = view_paths.dup + Array.wrap(path)
208
- end
209
-
210
- # Prepend a path to the list of view paths for this controller.
211
- #
212
- # ==== Parameters
213
- # path<String, ViewPath>:: If a String is provided, it gets converted into
214
- # the default view path. You may also provide a custom view path
215
- # (see ActionView::ViewPathSet for more information)
216
- def prepend_view_path(path)
217
- clear_template_caches!
218
- self.view_paths = Array.wrap(path) + view_paths.dup
219
- end
220
-
221
- # A list of all of the default view paths for this controller.
222
- def view_paths
223
- _view_paths
224
- end
225
-
226
- # Set the view paths.
227
- #
228
- # ==== Parameters
229
- # paths<ViewPathSet, Object>:: If a ViewPathSet is provided, use that;
230
- # otherwise, process the parameter into a ViewPathSet.
231
- def view_paths=(paths)
232
- clear_template_caches!
233
- self._view_paths = paths.is_a?(ActionView::PathSet) ? paths : ActionView::Base.process_view_paths(paths)
234
- _view_paths.freeze
235
- end
167
+ def _process_options(options)
236
168
  end
237
169
  end
238
170
  end