halorgium-actionpack 3.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (154) hide show
  1. data/CHANGELOG +5179 -0
  2. data/MIT-LICENSE +21 -0
  3. data/README +409 -0
  4. data/lib/abstract_controller.rb +16 -0
  5. data/lib/abstract_controller/base.rb +158 -0
  6. data/lib/abstract_controller/callbacks.rb +113 -0
  7. data/lib/abstract_controller/exceptions.rb +12 -0
  8. data/lib/abstract_controller/helpers.rb +151 -0
  9. data/lib/abstract_controller/layouts.rb +250 -0
  10. data/lib/abstract_controller/localized_cache.rb +49 -0
  11. data/lib/abstract_controller/logger.rb +61 -0
  12. data/lib/abstract_controller/rendering_controller.rb +188 -0
  13. data/lib/action_controller.rb +72 -0
  14. data/lib/action_controller/base.rb +168 -0
  15. data/lib/action_controller/caching.rb +80 -0
  16. data/lib/action_controller/caching/actions.rb +163 -0
  17. data/lib/action_controller/caching/fragments.rb +116 -0
  18. data/lib/action_controller/caching/pages.rb +154 -0
  19. data/lib/action_controller/caching/sweeping.rb +97 -0
  20. data/lib/action_controller/deprecated.rb +4 -0
  21. data/lib/action_controller/deprecated/integration_test.rb +2 -0
  22. data/lib/action_controller/deprecated/performance_test.rb +1 -0
  23. data/lib/action_controller/dispatch/dispatcher.rb +57 -0
  24. data/lib/action_controller/metal.rb +129 -0
  25. data/lib/action_controller/metal/benchmarking.rb +73 -0
  26. data/lib/action_controller/metal/compatibility.rb +145 -0
  27. data/lib/action_controller/metal/conditional_get.rb +86 -0
  28. data/lib/action_controller/metal/configuration.rb +28 -0
  29. data/lib/action_controller/metal/cookies.rb +105 -0
  30. data/lib/action_controller/metal/exceptions.rb +55 -0
  31. data/lib/action_controller/metal/filter_parameter_logging.rb +77 -0
  32. data/lib/action_controller/metal/flash.rb +162 -0
  33. data/lib/action_controller/metal/head.rb +27 -0
  34. data/lib/action_controller/metal/helpers.rb +115 -0
  35. data/lib/action_controller/metal/hide_actions.rb +47 -0
  36. data/lib/action_controller/metal/http_authentication.rb +312 -0
  37. data/lib/action_controller/metal/layouts.rb +171 -0
  38. data/lib/action_controller/metal/mime_responds.rb +317 -0
  39. data/lib/action_controller/metal/rack_convenience.rb +27 -0
  40. data/lib/action_controller/metal/redirector.rb +22 -0
  41. data/lib/action_controller/metal/render_options.rb +103 -0
  42. data/lib/action_controller/metal/rendering_controller.rb +57 -0
  43. data/lib/action_controller/metal/request_forgery_protection.rb +108 -0
  44. data/lib/action_controller/metal/rescuable.rb +13 -0
  45. data/lib/action_controller/metal/responder.rb +200 -0
  46. data/lib/action_controller/metal/session.rb +15 -0
  47. data/lib/action_controller/metal/session_management.rb +45 -0
  48. data/lib/action_controller/metal/streaming.rb +188 -0
  49. data/lib/action_controller/metal/testing.rb +39 -0
  50. data/lib/action_controller/metal/url_for.rb +41 -0
  51. data/lib/action_controller/metal/verification.rb +130 -0
  52. data/lib/action_controller/middleware.rb +38 -0
  53. data/lib/action_controller/notifications.rb +10 -0
  54. data/lib/action_controller/polymorphic_routes.rb +183 -0
  55. data/lib/action_controller/record_identifier.rb +91 -0
  56. data/lib/action_controller/testing/process.rb +111 -0
  57. data/lib/action_controller/testing/test_case.rb +345 -0
  58. data/lib/action_controller/translation.rb +13 -0
  59. data/lib/action_controller/url_rewriter.rb +204 -0
  60. data/lib/action_controller/vendor/html-scanner.rb +16 -0
  61. data/lib/action_controller/vendor/html-scanner/html/document.rb +68 -0
  62. data/lib/action_controller/vendor/html-scanner/html/node.rb +537 -0
  63. data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +176 -0
  64. data/lib/action_controller/vendor/html-scanner/html/selector.rb +828 -0
  65. data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +105 -0
  66. data/lib/action_controller/vendor/html-scanner/html/version.rb +11 -0
  67. data/lib/action_dispatch.rb +70 -0
  68. data/lib/action_dispatch/http/headers.rb +33 -0
  69. data/lib/action_dispatch/http/mime_type.rb +231 -0
  70. data/lib/action_dispatch/http/mime_types.rb +23 -0
  71. data/lib/action_dispatch/http/request.rb +539 -0
  72. data/lib/action_dispatch/http/response.rb +290 -0
  73. data/lib/action_dispatch/http/status_codes.rb +42 -0
  74. data/lib/action_dispatch/http/utils.rb +20 -0
  75. data/lib/action_dispatch/middleware/callbacks.rb +50 -0
  76. data/lib/action_dispatch/middleware/params_parser.rb +79 -0
  77. data/lib/action_dispatch/middleware/rescue.rb +26 -0
  78. data/lib/action_dispatch/middleware/session/abstract_store.rb +208 -0
  79. data/lib/action_dispatch/middleware/session/cookie_store.rb +235 -0
  80. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +47 -0
  81. data/lib/action_dispatch/middleware/show_exceptions.rb +143 -0
  82. data/lib/action_dispatch/middleware/stack.rb +116 -0
  83. data/lib/action_dispatch/middleware/static.rb +44 -0
  84. data/lib/action_dispatch/middleware/string_coercion.rb +29 -0
  85. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +24 -0
  86. data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +26 -0
  87. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb +10 -0
  88. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +29 -0
  89. data/lib/action_dispatch/middleware/templates/rescues/missing_template.erb +2 -0
  90. data/lib/action_dispatch/middleware/templates/rescues/routing_error.erb +10 -0
  91. data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +21 -0
  92. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb +2 -0
  93. data/lib/action_dispatch/routing.rb +381 -0
  94. data/lib/action_dispatch/routing/deprecated_mapper.rb +878 -0
  95. data/lib/action_dispatch/routing/mapper.rb +327 -0
  96. data/lib/action_dispatch/routing/route.rb +49 -0
  97. data/lib/action_dispatch/routing/route_set.rb +497 -0
  98. data/lib/action_dispatch/testing/assertions.rb +8 -0
  99. data/lib/action_dispatch/testing/assertions/dom.rb +35 -0
  100. data/lib/action_dispatch/testing/assertions/model.rb +19 -0
  101. data/lib/action_dispatch/testing/assertions/response.rb +145 -0
  102. data/lib/action_dispatch/testing/assertions/routing.rb +144 -0
  103. data/lib/action_dispatch/testing/assertions/selector.rb +639 -0
  104. data/lib/action_dispatch/testing/assertions/tag.rb +123 -0
  105. data/lib/action_dispatch/testing/integration.rb +504 -0
  106. data/lib/action_dispatch/testing/performance_test.rb +15 -0
  107. data/lib/action_dispatch/testing/test_request.rb +83 -0
  108. data/lib/action_dispatch/testing/test_response.rb +131 -0
  109. data/lib/action_pack.rb +24 -0
  110. data/lib/action_pack/version.rb +9 -0
  111. data/lib/action_view.rb +58 -0
  112. data/lib/action_view/base.rb +308 -0
  113. data/lib/action_view/context.rb +44 -0
  114. data/lib/action_view/erb/util.rb +48 -0
  115. data/lib/action_view/helpers.rb +62 -0
  116. data/lib/action_view/helpers/active_model_helper.rb +306 -0
  117. data/lib/action_view/helpers/ajax_helper.rb +68 -0
  118. data/lib/action_view/helpers/asset_tag_helper.rb +830 -0
  119. data/lib/action_view/helpers/atom_feed_helper.rb +198 -0
  120. data/lib/action_view/helpers/cache_helper.rb +39 -0
  121. data/lib/action_view/helpers/capture_helper.rb +168 -0
  122. data/lib/action_view/helpers/date_helper.rb +988 -0
  123. data/lib/action_view/helpers/debug_helper.rb +38 -0
  124. data/lib/action_view/helpers/form_helper.rb +1102 -0
  125. data/lib/action_view/helpers/form_options_helper.rb +600 -0
  126. data/lib/action_view/helpers/form_tag_helper.rb +495 -0
  127. data/lib/action_view/helpers/javascript_helper.rb +208 -0
  128. data/lib/action_view/helpers/number_helper.rb +311 -0
  129. data/lib/action_view/helpers/prototype_helper.rb +1309 -0
  130. data/lib/action_view/helpers/raw_output_helper.rb +9 -0
  131. data/lib/action_view/helpers/record_identification_helper.rb +20 -0
  132. data/lib/action_view/helpers/record_tag_helper.rb +58 -0
  133. data/lib/action_view/helpers/sanitize_helper.rb +259 -0
  134. data/lib/action_view/helpers/scriptaculous_helper.rb +226 -0
  135. data/lib/action_view/helpers/tag_helper.rb +151 -0
  136. data/lib/action_view/helpers/text_helper.rb +594 -0
  137. data/lib/action_view/helpers/translation_helper.rb +39 -0
  138. data/lib/action_view/helpers/url_helper.rb +639 -0
  139. data/lib/action_view/locale/en.yml +117 -0
  140. data/lib/action_view/paths.rb +80 -0
  141. data/lib/action_view/render/partials.rb +342 -0
  142. data/lib/action_view/render/rendering.rb +134 -0
  143. data/lib/action_view/safe_buffer.rb +28 -0
  144. data/lib/action_view/template/error.rb +101 -0
  145. data/lib/action_view/template/handler.rb +36 -0
  146. data/lib/action_view/template/handlers.rb +52 -0
  147. data/lib/action_view/template/handlers/builder.rb +17 -0
  148. data/lib/action_view/template/handlers/erb.rb +53 -0
  149. data/lib/action_view/template/handlers/rjs.rb +18 -0
  150. data/lib/action_view/template/resolver.rb +165 -0
  151. data/lib/action_view/template/template.rb +131 -0
  152. data/lib/action_view/template/text.rb +38 -0
  153. data/lib/action_view/test_case.rb +163 -0
  154. metadata +236 -0
@@ -0,0 +1,113 @@
1
+ module AbstractController
2
+ module Callbacks
3
+ extend ActiveSupport::Concern
4
+
5
+ # Uses ActiveSupport::Callbacks as the base functionality. For
6
+ # more details on the whole callback system, read the documentation
7
+ # for ActiveSupport::Callbacks.
8
+ include ActiveSupport::Callbacks
9
+
10
+ included do
11
+ define_callbacks :process_action, :terminator => "response_body"
12
+ end
13
+
14
+ # Override AbstractController::Base's process_action to run the
15
+ # process_action callbacks around the normal behavior.
16
+ def process_action(method_name)
17
+ run_callbacks(:process_action, method_name) do
18
+ super
19
+ end
20
+ end
21
+
22
+ module ClassMethods
23
+ # If :only or :accept are used, convert the options into the
24
+ # primitive form (:per_key) used by ActiveSupport::Callbacks.
25
+ # The basic idea is that :only => :index gets converted to
26
+ # :if => proc {|c| c.action_name == "index" }, but that the
27
+ # proc is only evaluated once per action for the lifetime of
28
+ # a Rails process.
29
+ #
30
+ # ==== Options
31
+ # :only<#to_s>:: The callback should be run only for this action
32
+ # :except<#to_s>:: The callback should be run for all actions
33
+ # except this action
34
+ def _normalize_callback_options(options)
35
+ if only = options[:only]
36
+ only = Array(only).map {|o| "action_name == '#{o}'"}.join(" || ")
37
+ options[:per_key] = {:if => only}
38
+ end
39
+ if except = options[:except]
40
+ except = Array(except).map {|e| "action_name == '#{e}'"}.join(" || ")
41
+ options[:per_key] = {:unless => except}
42
+ end
43
+ end
44
+
45
+ # Skip before, after, and around filters matching any of the names
46
+ #
47
+ # ==== Parameters
48
+ # *names<Object>:: A list of valid names that could be used for
49
+ # callbacks. Note that skipping uses Ruby equality, so it's
50
+ # impossible to skip a callback defined using an anonymous proc
51
+ # using #skip_filter
52
+ def skip_filter(*names, &blk)
53
+ skip_before_filter(*names)
54
+ skip_after_filter(*names)
55
+ skip_around_filter(*names)
56
+ end
57
+
58
+ # Take callback names and an optional callback proc, normalize them,
59
+ # then call the block with each callback. This allows us to abstract
60
+ # the normalization across several methods that use it.
61
+ #
62
+ # ==== Parameters
63
+ # callbacks<Array[*Object, Hash]>:: A list of callbacks, with an optional
64
+ # options hash as the last parameter.
65
+ # block<Proc>:: A proc that should be added to the callbacks.
66
+ #
67
+ # ==== Block Parameters
68
+ # name<Symbol>:: The callback to be added
69
+ # options<Hash>:: A list of options to be used when adding the callback
70
+ def _insert_callbacks(callbacks, block)
71
+ options = callbacks.last.is_a?(Hash) ? callbacks.pop : {}
72
+ _normalize_callback_options(options)
73
+ callbacks.push(block) if block
74
+ callbacks.each do |callback|
75
+ yield callback, options
76
+ end
77
+ end
78
+
79
+ # set up before_filter, prepend_before_filter, skip_before_filter, etc.
80
+ # for each of before, after, and around.
81
+ [:before, :after, :around].each do |filter|
82
+ class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
83
+ # Append a before, after or around filter. See _insert_callbacks
84
+ # for details on the allowed parameters.
85
+ def #{filter}_filter(*names, &blk)
86
+ _insert_callbacks(names, blk) do |name, options|
87
+ set_callback(:process_action, :#{filter}, name, options)
88
+ end
89
+ end
90
+
91
+ # Prepend a before, after or around filter. See _insert_callbacks
92
+ # for details on the allowed parameters.
93
+ def prepend_#{filter}_filter(*names, &blk)
94
+ _insert_callbacks(names, blk) do |name, options|
95
+ set_callback(:process_action, :#{filter}, name, options.merge(:prepend => true))
96
+ end
97
+ end
98
+
99
+ # Skip a before, after or around filter. See _insert_callbacks
100
+ # for details on the allowed parameters.
101
+ def skip_#{filter}_filter(*names, &blk)
102
+ _insert_callbacks(names, blk) do |name, options|
103
+ skip_callback(:process_action, :#{filter}, name, options)
104
+ end
105
+ end
106
+
107
+ # *_filter is the same as append_*_filter
108
+ alias_method :append_#{filter}_filter, :#{filter}_filter
109
+ RUBY_EVAL
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,12 @@
1
+ module AbstractController
2
+ class Error < StandardError; end
3
+ class ActionNotFound < StandardError; end
4
+
5
+ class DoubleRenderError < Error
6
+ DEFAULT_MESSAGE = "Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like \"redirect_to(...) and return\"."
7
+
8
+ def initialize(message = nil)
9
+ super(message || DEFAULT_MESSAGE)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,151 @@
1
+ require 'active_support/dependencies'
2
+
3
+ module AbstractController
4
+ module Helpers
5
+ extend ActiveSupport::Concern
6
+
7
+ include RenderingController
8
+
9
+ def self.next_serial
10
+ @helper_serial ||= 0
11
+ @helper_serial += 1
12
+ end
13
+
14
+ included do
15
+ extlib_inheritable_accessor(:_helpers) { Module.new }
16
+ extlib_inheritable_accessor(:_helper_serial) do
17
+ AbstractController::Helpers.next_serial
18
+ end
19
+ end
20
+
21
+ module ClassMethods
22
+ # When a class is inherited, wrap its helper module in a new module.
23
+ # This ensures that the parent class's module can be changed
24
+ # independently of the child class's.
25
+ def inherited(klass)
26
+ helpers = _helpers
27
+ klass._helpers = Module.new { include helpers }
28
+
29
+ super
30
+ end
31
+
32
+ # Declare a controller method as a helper. For example, the following
33
+ # makes the +current_user+ controller method available to the view:
34
+ # class ApplicationController < ActionController::Base
35
+ # helper_method :current_user, :logged_in?
36
+ #
37
+ # def current_user
38
+ # @current_user ||= User.find_by_id(session[:user])
39
+ # end
40
+ #
41
+ # def logged_in?
42
+ # current_user != nil
43
+ # end
44
+ # end
45
+ #
46
+ # In a view:
47
+ # <% if logged_in? -%>Welcome, <%= current_user.name %><% end -%>
48
+ #
49
+ # ==== Parameters
50
+ # meths<Array[#to_s]>:: The name of a method on the controller
51
+ # to be made available on the view.
52
+ def helper_method(*meths)
53
+ meths.flatten.each do |meth|
54
+ _helpers.class_eval <<-ruby_eval, __FILE__, __LINE__ + 1
55
+ def #{meth}(*args, &blk)
56
+ controller.send(%(#{meth}), *args, &blk)
57
+ end
58
+ ruby_eval
59
+ end
60
+ end
61
+
62
+ # The +helper+ class method can take a series of helper module names, a block, or both.
63
+ #
64
+ # ==== Parameters
65
+ # *args<Array[Module, Symbol, String, :all]>
66
+ # block<Block>:: A block defining helper methods
67
+ #
68
+ # ==== Examples
69
+ # When the argument is a module it will be included directly in the template class.
70
+ # helper FooHelper # => includes FooHelper
71
+ #
72
+ # When the argument is a string or symbol, the method will provide the "_helper" suffix, require the file
73
+ # and include the module in the template class. The second form illustrates how to include custom helpers
74
+ # when working with namespaced controllers, or other cases where the file containing the helper definition is not
75
+ # in one of Rails' standard load paths:
76
+ # helper :foo # => requires 'foo_helper' and includes FooHelper
77
+ # helper 'resources/foo' # => requires 'resources/foo_helper' and includes Resources::FooHelper
78
+ #
79
+ # Additionally, the +helper+ class method can receive and evaluate a block, making the methods defined available
80
+ # to the template.
81
+ #
82
+ # # One line
83
+ # helper { def hello() "Hello, world!" end }
84
+ #
85
+ # # Multi-line
86
+ # helper do
87
+ # def foo(bar)
88
+ # "#{bar} is the very best"
89
+ # end
90
+ # end
91
+ #
92
+ # Finally, all the above styles can be mixed together, and the +helper+ method can be invoked with a mix of
93
+ # +symbols+, +strings+, +modules+ and blocks.
94
+ #
95
+ # helper(:three, BlindHelper) { def mice() 'mice' end }
96
+ #
97
+ def helper(*args, &block)
98
+ self._helper_serial = AbstractController::Helpers.next_serial + 1
99
+
100
+ _modules_for_helpers(args).each do |mod|
101
+ add_template_helper(mod)
102
+ end
103
+
104
+ _helpers.module_eval(&block) if block_given?
105
+ end
106
+
107
+ private
108
+ # Makes all the (instance) methods in the helper module available to templates
109
+ # rendered through this controller.
110
+ #
111
+ # ==== Parameters
112
+ # mod<Module>:: The module to include into the current helper module
113
+ # for the class
114
+ def add_template_helper(mod)
115
+ _helpers.module_eval { include mod }
116
+ end
117
+
118
+ # Returns a list of modules, normalized from the acceptable kinds of
119
+ # helpers with the following behavior:
120
+ #
121
+ # String or Symbol:: :FooBar or "FooBar" becomes "foo_bar_helper",
122
+ # and "foo_bar_helper.rb" is loaded using require_dependency.
123
+ #
124
+ # Module:: No further processing
125
+ #
126
+ # After loading the appropriate files, the corresponding modules
127
+ # are returned.
128
+ #
129
+ # ==== Parameters
130
+ # args<Array[String, Symbol, Module]>:: A list of helpers
131
+ #
132
+ # ==== Returns
133
+ # Array[Module]:: A normalized list of modules for the list of
134
+ # helpers provided.
135
+ def _modules_for_helpers(args)
136
+ args.flatten.map! do |arg|
137
+ case arg
138
+ when String, Symbol
139
+ file_name = "#{arg.to_s.underscore}_helper"
140
+ require_dependency(file_name, "Missing helper file helpers/%s.rb")
141
+ file_name.camelize.constantize
142
+ when Module
143
+ arg
144
+ else
145
+ raise ArgumentError, "helper must be a String, Symbol, or Module"
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,250 @@
1
+ module AbstractController
2
+ module Layouts
3
+ extend ActiveSupport::Concern
4
+
5
+ include RenderingController
6
+
7
+ included do
8
+ extlib_inheritable_accessor(:_layout_conditions) { Hash.new }
9
+ extlib_inheritable_accessor(:_action_has_layout) { Hash.new }
10
+ _write_layout_method
11
+ end
12
+
13
+ module ClassMethods
14
+ def inherited(klass)
15
+ super
16
+ klass.class_eval do
17
+ _write_layout_method
18
+ @found_layouts = {}
19
+ end
20
+ end
21
+
22
+ def clear_template_caches!
23
+ @found_layouts.clear if @found_layouts
24
+ super
25
+ end
26
+
27
+ def cache_layout(details)
28
+ layout = @found_layouts
29
+ key = Thread.current[:format_locale_key]
30
+
31
+ # Cache nil
32
+ if layout.key?(key)
33
+ return layout[key]
34
+ else
35
+ layout[key] = yield
36
+ end
37
+ end
38
+
39
+ # This module is mixed in if layout conditions are provided. This means
40
+ # that if no layout conditions are used, this method is not used
41
+ module LayoutConditions
42
+ # Determines whether the current action has a layout by checking the
43
+ # action name against the :only and :except conditions set on the
44
+ # layout.
45
+ #
46
+ # ==== Returns
47
+ # Boolean:: True if the action has a layout, false otherwise.
48
+ def _action_has_layout?
49
+ conditions = _layout_conditions
50
+
51
+ if only = conditions[:only]
52
+ only.include?(action_name)
53
+ elsif except = conditions[:except]
54
+ !except.include?(action_name)
55
+ else
56
+ true
57
+ end
58
+ end
59
+ end
60
+
61
+ # Specify the layout to use for this class.
62
+ #
63
+ # If the specified layout is a:
64
+ # String:: the String is the template name
65
+ # Symbol:: call the method specified by the symbol, which will return
66
+ # the template name
67
+ # false:: There is no layout
68
+ # true:: raise an ArgumentError
69
+ #
70
+ # ==== Parameters
71
+ # layout<String, Symbol, false)>:: The layout to use.
72
+ #
73
+ # ==== Options (conditions)
74
+ # :only<#to_s, Array[#to_s]>:: A list of actions to apply this layout to.
75
+ # :except<#to_s, Array[#to_s]>:: Apply this layout to all actions but this one
76
+ def layout(layout, conditions = {})
77
+ include LayoutConditions unless conditions.empty?
78
+
79
+ conditions.each {|k, v| conditions[k] = Array(v).map {|a| a.to_s} }
80
+ self._layout_conditions = conditions
81
+
82
+ @_layout = layout || false # Converts nil to false
83
+ _write_layout_method
84
+ end
85
+
86
+ # If no layout is supplied, look for a template named the return
87
+ # value of this method.
88
+ #
89
+ # ==== Returns
90
+ # String:: A template name
91
+ def _implied_layout_name
92
+ name && name.underscore
93
+ end
94
+
95
+ # Takes the specified layout and creates a _layout method to be called
96
+ # by _default_layout
97
+ #
98
+ # If there is no explicit layout specified:
99
+ # If a layout is found in the view paths with the controller's
100
+ # name, return that string. Otherwise, use the superclass'
101
+ # layout (which might also be implied)
102
+ def _write_layout_method
103
+ case @_layout
104
+ when String
105
+ self.class_eval %{def _layout(details) #{@_layout.inspect} end}
106
+ when Symbol
107
+ self.class_eval <<-ruby_eval, __FILE__, __LINE__ + 1
108
+ def _layout(details)
109
+ #{@_layout}.tap do |layout|
110
+ unless layout.is_a?(String) || !layout
111
+ raise ArgumentError, "Your layout method :#{@_layout} returned \#{layout}. It " \
112
+ "should have returned a String, false, or nil"
113
+ end
114
+ end
115
+ end
116
+ ruby_eval
117
+ when false
118
+ self.class_eval %{def _layout(details) end}
119
+ when true
120
+ raise ArgumentError, "Layouts must be specified as a String, Symbol, false, or nil"
121
+ when nil
122
+ if name
123
+ self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
124
+ def _layout(details)
125
+ self.class.cache_layout(details) do
126
+ if template_exists?("#{_implied_layout_name}", details, :_prefix => "layouts")
127
+ "#{_implied_layout_name}"
128
+ else
129
+ super
130
+ end
131
+ end
132
+ end
133
+ RUBY
134
+ end
135
+ end
136
+ self.class_eval { private :_layout }
137
+ end
138
+ end
139
+
140
+ def render_to_body(options = {})
141
+ # In the case of a partial with a layout, handle the layout
142
+ # here, and make sure the view does not try to handle it
143
+ layout = options.delete(:layout) if options.key?(:partial)
144
+
145
+ response = super
146
+
147
+ # This is a little bit messy. We need to explicitly handle partial
148
+ # layouts here since the core lookup logic is in the view, but
149
+ # we need to determine the layout based on the controller
150
+ #
151
+ # TODO: An easier way to handle this would probably be to override
152
+ # render_template
153
+ if layout
154
+ layout = _layout_for_option(layout, options[:_template].details)
155
+ response = layout.render(view_context, options[:locals] || {}) { response }
156
+ end
157
+
158
+ response
159
+ end
160
+
161
+ private
162
+
163
+ # This will be overwritten by _write_layout_method
164
+ def _layout(details) end
165
+
166
+ # Determine the layout for a given name and details.
167
+ #
168
+ # ==== Parameters
169
+ # name<String>:: The name of the template
170
+ # details<Hash{Symbol => Object}>:: A list of details to restrict
171
+ # the lookup to. By default, layout lookup is limited to the
172
+ # formats specified for the current request.
173
+ def _layout_for_name(name, details)
174
+ name && _find_layout(name, details)
175
+ end
176
+
177
+ # Determine the layout for a given name and details, taking into account
178
+ # the name type.
179
+ #
180
+ # ==== Parameters
181
+ # name<String|TrueClass|FalseClass|Symbol>:: The name of the template
182
+ # details<Hash{Symbol => Object}>:: A list of details to restrict
183
+ # the lookup to. By default, layout lookup is limited to the
184
+ # formats specified for the current request.
185
+ def _layout_for_option(name, details)
186
+ case name
187
+ when String then _layout_for_name(name, details)
188
+ when true then _default_layout(details, true)
189
+ when :default then _default_layout(details, false)
190
+ when false, nil then nil
191
+ else
192
+ raise ArgumentError,
193
+ "String, true, or false, expected for `layout'; you passed #{name.inspect}"
194
+ end
195
+ end
196
+
197
+ def _determine_template(options)
198
+ super
199
+
200
+ return unless (options.keys & [:text, :inline, :partial]).empty? || options.key?(:layout)
201
+ layout = options.key?(:layout) ? options[:layout] : :default
202
+ options[:_layout] = _layout_for_option(layout, options[:_template].details)
203
+ end
204
+
205
+ # Take in the name and details and find a Template.
206
+ #
207
+ # ==== Parameters
208
+ # name<String>:: The name of the template to retrieve
209
+ # details<Hash>:: A list of details to restrict the search by. This
210
+ # might include details like the format or locale of the template.
211
+ #
212
+ # ==== Returns
213
+ # Template:: A template object matching the name and details
214
+ def _find_layout(name, details)
215
+ # TODO: Make prefix actually part of details in ViewPath#find_by_parts
216
+ prefix = details.key?(:prefix) ? details.delete(:prefix) : "layouts"
217
+ find_template(name, details, :_prefix => prefix)
218
+ end
219
+
220
+ # Returns the default layout for this controller and a given set of details.
221
+ # Optionally raises an exception if the layout could not be found.
222
+ #
223
+ # ==== Parameters
224
+ # details<Hash>:: A list of details to restrict the search by. This
225
+ # might include details like the format or locale of the template.
226
+ # require_layout<Boolean>:: If this is true, raise an ArgumentError
227
+ # with details about the fact that the exception could not be
228
+ # found (defaults to false)
229
+ #
230
+ # ==== Returns
231
+ # Template:: The template object for the default layout (or nil)
232
+ def _default_layout(details, require_layout = false)
233
+ if require_layout && _action_has_layout? && !_layout(details)
234
+ raise ArgumentError,
235
+ "There was no default layout for #{self.class} in #{view_paths.inspect}"
236
+ end
237
+
238
+ begin
239
+ _layout_for_name(_layout(details), details) if _action_has_layout?
240
+ rescue NameError => e
241
+ raise NoMethodError,
242
+ "You specified #{@_layout.inspect} as the layout, but no such method was found"
243
+ end
244
+ end
245
+
246
+ def _action_has_layout?
247
+ true
248
+ end
249
+ end
250
+ end