actionpack 3.0.20 → 3.1.0.beta1

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 (161) hide show
  1. data/CHANGELOG +88 -142
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +5 -6
  4. data/lib/abstract_controller.rb +1 -0
  5. data/lib/abstract_controller/asset_paths.rb +2 -2
  6. data/lib/abstract_controller/base.rb +24 -19
  7. data/lib/abstract_controller/callbacks.rb +19 -19
  8. data/lib/abstract_controller/helpers.rb +11 -13
  9. data/lib/abstract_controller/layouts.rb +4 -5
  10. data/lib/abstract_controller/railties/routes_helpers.rb +18 -0
  11. data/lib/abstract_controller/rendering.rb +34 -31
  12. data/lib/abstract_controller/url_for.rb +27 -0
  13. data/lib/abstract_controller/view_paths.rb +31 -6
  14. data/lib/action_controller.rb +5 -3
  15. data/lib/action_controller/base.rb +15 -16
  16. data/lib/action_controller/caching.rb +2 -2
  17. data/lib/action_controller/caching/actions.rb +11 -12
  18. data/lib/action_controller/caching/fragments.rb +41 -19
  19. data/lib/action_controller/caching/pages.rb +3 -9
  20. data/lib/action_controller/caching/sweeping.rb +0 -1
  21. data/lib/action_controller/deprecated.rb +1 -1
  22. data/lib/action_controller/log_subscriber.rb +1 -1
  23. data/lib/action_controller/metal.rb +78 -20
  24. data/lib/action_controller/metal/compatibility.rb +0 -9
  25. data/lib/action_controller/metal/conditional_get.rb +9 -9
  26. data/lib/action_controller/metal/data_streaming.rb +145 -0
  27. data/lib/action_controller/metal/force_ssl.rb +35 -0
  28. data/lib/action_controller/metal/head.rb +1 -1
  29. data/lib/action_controller/metal/helpers.rb +37 -44
  30. data/lib/action_controller/metal/hide_actions.rb +2 -3
  31. data/lib/action_controller/metal/http_authentication.rb +41 -38
  32. data/lib/action_controller/metal/implicit_render.rb +13 -13
  33. data/lib/action_controller/metal/instrumentation.rb +2 -2
  34. data/lib/action_controller/metal/mime_responds.rb +25 -19
  35. data/lib/action_controller/metal/params_wrapper.rb +224 -0
  36. data/lib/action_controller/metal/redirecting.rb +6 -2
  37. data/lib/action_controller/metal/renderers.rb +50 -36
  38. data/lib/action_controller/metal/rendering.rb +34 -25
  39. data/lib/action_controller/metal/request_forgery_protection.rb +18 -36
  40. data/lib/action_controller/metal/responder.rb +47 -12
  41. data/lib/action_controller/metal/streaming.rb +244 -138
  42. data/lib/action_controller/metal/testing.rb +0 -9
  43. data/lib/action_controller/metal/url_for.rb +12 -14
  44. data/lib/action_controller/railtie.rb +19 -37
  45. data/lib/action_controller/railties/paths.rb +24 -0
  46. data/lib/action_controller/record_identifier.rb +4 -10
  47. data/lib/action_controller/test_case.rb +36 -19
  48. data/lib/action_controller/vendor/html-scanner/html/node.rb +5 -5
  49. data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +3 -3
  50. data/lib/action_controller/vendor/html-scanner/html/selector.rb +2 -0
  51. data/lib/action_dispatch.rb +4 -1
  52. data/lib/action_dispatch/http/cache.rb +5 -32
  53. data/lib/action_dispatch/http/filter_parameters.rb +3 -1
  54. data/lib/action_dispatch/http/mime_negotiation.rb +22 -3
  55. data/lib/action_dispatch/http/mime_type.rb +45 -5
  56. data/lib/action_dispatch/http/rack_cache.rb +58 -0
  57. data/lib/action_dispatch/http/request.rb +27 -41
  58. data/lib/action_dispatch/http/response.rb +56 -54
  59. data/lib/action_dispatch/http/upload.rb +1 -11
  60. data/lib/action_dispatch/http/url.rb +102 -42
  61. data/lib/action_dispatch/middleware/callbacks.rb +8 -25
  62. data/lib/action_dispatch/middleware/closed_error.rb +7 -0
  63. data/lib/action_dispatch/middleware/cookies.rb +37 -15
  64. data/lib/action_dispatch/middleware/flash.rb +80 -11
  65. data/lib/action_dispatch/middleware/params_parser.rb +2 -2
  66. data/lib/action_dispatch/middleware/reloader.rb +76 -0
  67. data/lib/action_dispatch/middleware/session/abstract_store.rb +56 -226
  68. data/lib/action_dispatch/middleware/session/cookie_store.rb +20 -44
  69. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +7 -46
  70. data/lib/action_dispatch/middleware/show_exceptions.rb +15 -2
  71. data/lib/action_dispatch/middleware/stack.rb +50 -17
  72. data/lib/action_dispatch/middleware/static.rb +41 -29
  73. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +3 -3
  74. data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +3 -3
  75. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb +3 -3
  76. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +4 -2
  77. data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +2 -6
  78. data/lib/action_dispatch/railtie.rb +8 -0
  79. data/lib/action_dispatch/routing.rb +13 -1
  80. data/lib/action_dispatch/routing/mapper.rb +345 -227
  81. data/lib/action_dispatch/routing/polymorphic_routes.rb +33 -13
  82. data/lib/action_dispatch/routing/redirection.rb +110 -0
  83. data/lib/action_dispatch/routing/route.rb +15 -13
  84. data/lib/action_dispatch/routing/route_set.rb +116 -90
  85. data/lib/action_dispatch/routing/routes_proxy.rb +35 -0
  86. data/lib/action_dispatch/routing/url_for.rb +25 -1
  87. data/lib/action_dispatch/testing/assertions/response.rb +8 -10
  88. data/lib/action_dispatch/testing/assertions/routing.rb +15 -15
  89. data/lib/action_dispatch/testing/assertions/selector.rb +13 -220
  90. data/lib/action_dispatch/testing/integration.rb +37 -28
  91. data/lib/action_dispatch/testing/performance_test.rb +1 -3
  92. data/lib/action_dispatch/testing/test_process.rb +1 -1
  93. data/lib/action_dispatch/testing/test_request.rb +9 -3
  94. data/lib/action_dispatch/testing/test_response.rb +4 -111
  95. data/lib/action_pack.rb +1 -1
  96. data/lib/action_pack/version.rb +3 -3
  97. data/lib/action_view.rb +39 -24
  98. data/lib/action_view/base.rb +61 -86
  99. data/lib/action_view/buffers.rb +43 -0
  100. data/lib/action_view/context.rb +21 -24
  101. data/lib/action_view/flows.rb +79 -0
  102. data/lib/action_view/helpers.rb +8 -6
  103. data/lib/action_view/helpers/active_model_helper.rb +0 -23
  104. data/lib/action_view/helpers/asset_paths.rb +79 -0
  105. data/lib/action_view/helpers/asset_tag_helper.rb +30 -500
  106. data/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb +147 -0
  107. data/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb +101 -0
  108. data/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb +200 -0
  109. data/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb +152 -0
  110. data/lib/action_view/helpers/atom_feed_helper.rb +2 -2
  111. data/lib/action_view/helpers/cache_helper.rb +11 -19
  112. data/lib/action_view/helpers/capture_helper.rb +19 -8
  113. data/lib/action_view/helpers/controller_helper.rb +21 -0
  114. data/lib/action_view/helpers/csrf_helper.rb +22 -4
  115. data/lib/action_view/helpers/date_helper.rb +36 -22
  116. data/lib/action_view/helpers/form_helper.rb +199 -113
  117. data/lib/action_view/helpers/form_options_helper.rb +10 -11
  118. data/lib/action_view/helpers/form_tag_helper.rb +94 -22
  119. data/lib/action_view/helpers/javascript_helper.rb +24 -107
  120. data/lib/action_view/helpers/number_helper.rb +36 -33
  121. data/lib/action_view/helpers/output_safety_helper.rb +38 -0
  122. data/lib/action_view/helpers/record_tag_helper.rb +6 -6
  123. data/lib/action_view/helpers/rendering_helper.rb +90 -0
  124. data/lib/action_view/helpers/sanitize_helper.rb +2 -2
  125. data/lib/action_view/helpers/sprockets_helper.rb +69 -0
  126. data/lib/action_view/helpers/tag_helper.rb +34 -12
  127. data/lib/action_view/helpers/text_helper.rb +30 -145
  128. data/lib/action_view/helpers/translation_helper.rb +10 -17
  129. data/lib/action_view/helpers/url_helper.rb +70 -67
  130. data/lib/action_view/locale/en.yml +1 -1
  131. data/lib/action_view/lookup_context.rb +36 -14
  132. data/lib/action_view/{paths.rb → path_set.rb} +9 -8
  133. data/lib/action_view/railtie.rb +12 -4
  134. data/lib/action_view/renderer/abstract_renderer.rb +36 -0
  135. data/lib/action_view/{render/partials.rb → renderer/partial_renderer.rb} +147 -146
  136. data/lib/action_view/renderer/renderer.rb +54 -0
  137. data/lib/action_view/renderer/streaming_template_renderer.rb +106 -0
  138. data/lib/action_view/renderer/template_renderer.rb +74 -0
  139. data/lib/action_view/template.rb +91 -54
  140. data/lib/action_view/template/error.rb +11 -8
  141. data/lib/action_view/template/handler.rb +9 -1
  142. data/lib/action_view/template/handlers.rb +9 -9
  143. data/lib/action_view/template/handlers/builder.rb +4 -4
  144. data/lib/action_view/template/handlers/erb.rb +21 -41
  145. data/lib/action_view/template/resolver.rb +171 -57
  146. data/lib/action_view/template/text.rb +0 -4
  147. data/lib/action_view/test_case.rb +32 -16
  148. data/lib/action_view/testing/resolvers.rb +16 -10
  149. data/lib/sprockets/railtie.rb +100 -0
  150. metadata +162 -140
  151. checksums.yaml +0 -7
  152. data/lib/action_controller/deprecated/base.rb +0 -143
  153. data/lib/action_controller/deprecated/dispatcher.rb +0 -28
  154. data/lib/action_controller/deprecated/url_writer.rb +0 -14
  155. data/lib/action_dispatch/routing/deprecated_mapper.rb +0 -525
  156. data/lib/action_view/helpers/prototype_helper.rb +0 -851
  157. data/lib/action_view/helpers/raw_output_helper.rb +0 -18
  158. data/lib/action_view/helpers/scriptaculous_helper.rb +0 -263
  159. data/lib/action_view/render/layouts.rb +0 -83
  160. data/lib/action_view/render/rendering.rb +0 -67
  161. data/lib/action_view/template/handlers/rjs.rb +0 -17
@@ -0,0 +1,54 @@
1
+ module ActionView
2
+ # This is the main entry point for rendering. It basically delegates
3
+ # to other objects like TemplateRenderer and PartialRenderer which
4
+ # actually renders the template.
5
+ class Renderer
6
+ attr_accessor :lookup_context
7
+
8
+ def initialize(lookup_context)
9
+ @lookup_context = lookup_context
10
+ end
11
+
12
+ # Main render entry point shared by AV and AC.
13
+ def render(context, options)
14
+ if options.key?(:partial)
15
+ render_partial(context, options)
16
+ else
17
+ render_template(context, options)
18
+ end
19
+ end
20
+
21
+ # Render but returns a valid Rack body. If fibers are defined, we return
22
+ # a streaming body that renders the template piece by piece.
23
+ #
24
+ # Note that partials are not supported to be rendered with streaming,
25
+ # so in such cases, we just wrap them in an array.
26
+ def render_body(context, options)
27
+ if options.key?(:partial)
28
+ [render_partial(context, options)]
29
+ else
30
+ StreamingTemplateRenderer.new(@lookup_context).render(context, options)
31
+ end
32
+ end
33
+
34
+ # Direct accessor to template rendering.
35
+ def render_template(context, options) #:nodoc:
36
+ _template_renderer.render(context, options)
37
+ end
38
+
39
+ # Direct access to partial rendering.
40
+ def render_partial(context, options, &block) #:nodoc:
41
+ _partial_renderer.render(context, options, block)
42
+ end
43
+
44
+ private
45
+
46
+ def _template_renderer #:nodoc:
47
+ @_template_renderer ||= TemplateRenderer.new(@lookup_context)
48
+ end
49
+
50
+ def _partial_renderer #:nodoc:
51
+ @_partial_renderer ||= PartialRenderer.new(@lookup_context)
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,106 @@
1
+ # 1.9 ships with Fibers but we need to require the extra
2
+ # methods explicitly. We only load those extra methods if
3
+ # Fiber is available in the first place.
4
+ require 'fiber' if defined?(Fiber)
5
+
6
+ module ActionView
7
+ # == TODO
8
+ #
9
+ # * Support streaming from child templates, partials and so on.
10
+ # * Integrate exceptions with exceptron
11
+ # * Rack::Cache needs to support streaming bodies
12
+ class StreamingTemplateRenderer < TemplateRenderer #:nodoc:
13
+ # A valid Rack::Body (i.e. it responds to each).
14
+ # It is initialized with a block that, when called, starts
15
+ # rendering the template.
16
+ class Body #:nodoc:
17
+ def initialize(&start)
18
+ @start = start
19
+ end
20
+
21
+ def each(&block)
22
+ begin
23
+ @start.call(block)
24
+ rescue Exception => exception
25
+ log_error(exception)
26
+ block.call ActionView::Base.streaming_completion_on_exception
27
+ end
28
+ self
29
+ end
30
+
31
+ private
32
+
33
+ # This is the same logging logic as in ShowExceptions middleware.
34
+ # TODO Once "exceptron" is in, refactor this piece to simply re-use exceptron.
35
+ def log_error(exception) #:nodoc:
36
+ logger = ActionController::Base.logger
37
+ return unless logger
38
+
39
+ message = "\n#{exception.class} (#{exception.message}):\n"
40
+ message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code)
41
+ message << " " << exception.backtrace.join("\n ")
42
+ logger.fatal("#{message}\n\n")
43
+ end
44
+ end
45
+
46
+ # For streaming, instead of rendering a given a template, we return a Body
47
+ # object that responds to each. This object is initialized with a block
48
+ # that knows how to render the template.
49
+ def render_template(template, layout_name = nil, locals = {}) #:nodoc:
50
+ return [super] unless layout_name && template.supports_streaming?
51
+
52
+ locals ||= {}
53
+ layout = layout_name && find_layout(layout_name, locals.keys)
54
+
55
+ Body.new do |buffer|
56
+ delayed_render(buffer, template, layout, @view, locals)
57
+ end
58
+ end
59
+
60
+ private
61
+
62
+ def delayed_render(buffer, template, layout, view, locals)
63
+ # Wrap the given buffer in the StreamingBuffer and pass it to the
64
+ # underlying template handler. Now, everytime something is concatenated
65
+ # to the buffer, it is not appended to an array, but streamed straight
66
+ # to the client.
67
+ output = ActionView::StreamingBuffer.new(buffer)
68
+ yielder = lambda { |*name| view._layout_for(*name) }
69
+
70
+ instrument(:template, :identifier => template.identifier, :layout => layout.try(:virtual_path)) do
71
+ fiber = Fiber.new do
72
+ if layout
73
+ layout.render(view, locals, output, &yielder)
74
+ else
75
+ # If you don't have a layout, just render the thing
76
+ # and concatenate the final result. This is the same
77
+ # as a layout with just <%= yield %>
78
+ output.safe_concat view._layout_for
79
+ end
80
+ end
81
+
82
+ # Set the view flow to support streaming. It will be aware
83
+ # when to stop rendering the layout because it needs to search
84
+ # something in the template and vice-versa.
85
+ view.view_flow = StreamingFlow.new(view, fiber)
86
+
87
+ # Yo! Start the fiber!
88
+ fiber.resume
89
+
90
+ # If the fiber is still alive, it means we need something
91
+ # from the template, so start rendering it. If not, it means
92
+ # the layout exited without requiring anything from the template.
93
+ if fiber.alive?
94
+ content = template.render(view, locals, &yielder)
95
+
96
+ # Once rendering the template is done, sets its content in the :layout key.
97
+ view.view_flow.set(:layout, content)
98
+
99
+ # In case the layout continues yielding, we need to resume
100
+ # the fiber until all yields are handled.
101
+ fiber.resume while fiber.alive?
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,74 @@
1
+ require 'active_support/core_ext/object/try'
2
+ require 'active_support/core_ext/array/wrap'
3
+
4
+ module ActionView
5
+ class TemplateRenderer < AbstractRenderer #:nodoc:
6
+ def render(context, options)
7
+ @view = context
8
+
9
+ wrap_formats(options[:template] || options[:file]) do
10
+ template = determine_template(options)
11
+ freeze_formats(template.formats, true)
12
+ render_template(template, options[:layout], options[:locals])
13
+ end
14
+ end
15
+
16
+ # Determine the template to be rendered using the given options.
17
+ def determine_template(options) #:nodoc:
18
+ keys = options[:locals].try(:keys) || []
19
+
20
+ if options.key?(:text)
21
+ Template::Text.new(options[:text], formats.try(:first))
22
+ elsif options.key?(:file)
23
+ with_fallbacks { find_template(options[:file], nil, false, keys) }
24
+ elsif options.key?(:inline)
25
+ handler = Template.handler_for_extension(options[:type] || "erb")
26
+ Template.new(options[:inline], "inline template", handler, :locals => keys)
27
+ elsif options.key?(:template)
28
+ options[:template].respond_to?(:render) ?
29
+ options[:template] : find_template(options[:template], options[:prefixes], false, keys)
30
+ end
31
+ end
32
+
33
+ # Renders the given template. An string representing the layout can be
34
+ # supplied as well.
35
+ def render_template(template, layout_name = nil, locals = {}) #:nodoc:
36
+ view, locals = @view, locals || {}
37
+
38
+ render_with_layout(layout_name, locals) do |layout|
39
+ instrument(:template, :identifier => template.identifier, :layout => layout.try(:virtual_path)) do
40
+ template.render(view, locals) { |*name| view._layout_for(*name) }
41
+ end
42
+ end
43
+ end
44
+
45
+ def render_with_layout(path, locals) #:nodoc:
46
+ layout = path && find_layout(path, locals.keys)
47
+ content = yield(layout)
48
+
49
+ if layout
50
+ view = @view
51
+ view.view_flow.set(:layout, content)
52
+ layout.render(view, locals){ |*name| view._layout_for(*name) }
53
+ else
54
+ content
55
+ end
56
+ end
57
+
58
+ # This is the method which actually finds the layout using details in the lookup
59
+ # context object. If no layout is found, it checks if at least a layout with
60
+ # the given name exists across all details before raising the error.
61
+ def find_layout(layout, keys)
62
+ begin
63
+ with_layout_format do
64
+ layout =~ /^\// ?
65
+ with_fallbacks { find_template(layout, nil, false, keys) } : find_template(layout, nil, false, keys)
66
+ end
67
+ rescue ActionView::MissingTemplate => e
68
+ update_details(:formats => nil) do
69
+ raise unless template_exists?(layout)
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -98,9 +98,12 @@ module ActionView
98
98
 
99
99
  extend Template::Handlers
100
100
 
101
- attr_reader :source, :identifier, :handler, :virtual_path, :formats,
102
- :original_encoding
101
+ attr_accessor :locals, :formats, :virtual_path
103
102
 
103
+ attr_reader :source, :identifier, :handler, :original_encoding, :updated_at
104
+
105
+ # This finalizer is needed (and exactly with a proc inside another proc)
106
+ # otherwise templates leak in development.
104
107
  Finalizer = proc do |method_name, mod|
105
108
  proc do
106
109
  mod.module_eval do
@@ -110,61 +113,88 @@ module ActionView
110
113
  end
111
114
 
112
115
  def initialize(source, identifier, handler, details)
113
- @source = source
114
- @identifier = identifier
115
- @handler = handler
116
- @original_encoding = nil
117
- @method_names = {}
116
+ format = details[:format] || (handler.default_format if handler.respond_to?(:default_format))
118
117
 
119
- format = details[:format] || :html
118
+ @source = source
119
+ @identifier = identifier
120
+ @handler = handler
121
+ @compiled = false
122
+ @original_encoding = nil
123
+ @locals = details[:locals] || []
124
+ @virtual_path = details[:virtual_path]
125
+ @updated_at = details[:updated_at] || Time.now
120
126
  @formats = Array.wrap(format).map { |f| f.is_a?(Mime::Type) ? f.ref : f }
121
- @virtual_path = details[:virtual_path].try(:sub, ".#{format}", "")
122
127
  end
123
128
 
124
- def render(view, locals, &block)
125
- # Notice that we use a bang in this instrumentation because you don't want to
126
- # consume this in production. This is only slow if it's being listened to.
127
- ActiveSupport::Notifications.instrument("!render_template.action_view", :virtual_path => @virtual_path) do
128
- if view.is_a?(ActionView::CompiledTemplates)
129
- mod = ActionView::CompiledTemplates
130
- else
131
- mod = view.singleton_class
132
- end
129
+ # Returns if the underlying handler supports streaming. If so,
130
+ # a streaming buffer *may* be passed when it start rendering.
131
+ def supports_streaming?
132
+ handler.respond_to?(:supports_streaming?) && handler.supports_streaming?
133
+ end
133
134
 
134
- method_name = compile(locals, view, mod)
135
- view.send(method_name, locals, &block)
135
+ # Render a template. If the template was not compiled yet, it is done
136
+ # exactly before rendering.
137
+ #
138
+ # This method is instrumented as "!render_template.action_view". Notice that
139
+ # we use a bang in this instrumentation because you don't want to
140
+ # consume this in production. This is only slow if it's being listened to.
141
+ def render(view, locals, buffer=nil, &block)
142
+ ActiveSupport::Notifications.instrument("!render_template.action_view", :virtual_path => @virtual_path) do
143
+ compile!(view)
144
+ view.send(method_name, locals, buffer, &block)
136
145
  end
137
146
  rescue Exception => e
138
- if e.is_a?(Template::Error)
139
- e.sub_template_of(self)
140
- raise e
141
- else
142
- raise Template::Error.new(self, view.respond_to?(:assigns) ? view.assigns : {}, e)
143
- end
147
+ handle_render_error(view, e)
144
148
  end
145
149
 
146
150
  def mime_type
147
151
  @mime_type ||= Mime::Type.lookup_by_extension(@formats.first.to_s) if @formats.first
148
152
  end
149
153
 
150
- def variable_name
151
- @variable_name ||= @virtual_path[%r'_?(\w+)(\.\w+)*$', 1].to_sym
154
+ # Receives a view object and return a template similar to self by using @virtual_path.
155
+ #
156
+ # This method is useful if you have a template object but it does not contain its source
157
+ # anymore since it was already compiled. In such cases, all you need to do is to call
158
+ # refresh passing in the view object.
159
+ #
160
+ # Notice this method raises an error if the template to be refreshed does not have a
161
+ # virtual path set (true just for inline templates).
162
+ def refresh(view)
163
+ raise "A template needs to have a virtual path in order to be refreshed" unless @virtual_path
164
+ lookup = view.lookup_context
165
+ pieces = @virtual_path.split("/")
166
+ name = pieces.pop
167
+ partial = !!name.sub!(/^_/, "")
168
+ lookup.disable_cache do
169
+ lookup.find_template(name, [ pieces.join('/') ], partial, @locals)
170
+ end
152
171
  end
153
172
 
154
- def counter_name
155
- @counter_name ||= "#{variable_name}_counter".to_sym
173
+ def inspect
174
+ @inspect ||= defined?(Rails.root) ? identifier.sub("#{Rails.root}/", '') : identifier
156
175
  end
157
176
 
158
- def inspect
159
- @inspect ||=
160
- if defined?(Rails.root)
161
- identifier.sub("#{Rails.root}/", '')
177
+ protected
178
+
179
+ # Compile a template. This method ensures a template is compiled
180
+ # just once and removes the source after it is compiled.
181
+ def compile!(view) #:nodoc:
182
+ return if @compiled
183
+
184
+ if view.is_a?(ActionView::CompiledTemplates)
185
+ mod = ActionView::CompiledTemplates
162
186
  else
163
- identifier
187
+ mod = view.singleton_class
164
188
  end
165
- end
166
189
 
167
- private
190
+ compile(view, mod)
191
+
192
+ # Just discard the source if we have a virtual path. This
193
+ # means we can get the template back.
194
+ @source = nil if @virtual_path
195
+ @compiled = true
196
+ end
197
+
168
198
  # Among other things, this method is responsible for properly setting
169
199
  # the encoding of the source. Until this point, we assume that the
170
200
  # source is BINARY data. If no additional information is supplied,
@@ -185,16 +215,10 @@ module ActionView
185
215
  # encode the source into Encoding.default_internal. In general,
186
216
  # this means that templates will be UTF-8 inside of Rails,
187
217
  # regardless of the original source encoding.
188
- def compile(locals, view, mod)
189
- method_name = build_method_name(locals)
190
- return method_name if view.respond_to?(method_name)
191
-
192
- locals_code = locals.keys.map! { |key| "#{key} = local_assigns[:#{key}];" }.join
218
+ def compile(view, mod) #:nodoc:
219
+ method_name = self.method_name
193
220
 
194
221
  if source.encoding_aware?
195
- # Avoid performing in-place mutation for SafeBuffer
196
- @source = source.to_str if source.html_safe?
197
-
198
222
  # Look for # encoding: *. If we find one, we'll encode the
199
223
  # String in that encoding, otherwise, we'll use the
200
224
  # default external encoding.
@@ -231,10 +255,10 @@ module ActionView
231
255
  # Make sure that the resulting String to be evalled is in the
232
256
  # encoding of the code
233
257
  source = <<-end_src
234
- def #{method_name}(local_assigns)
235
- _old_virtual_path, @_virtual_path = @_virtual_path, #{@virtual_path.inspect};_old_output_buffer = @output_buffer;#{locals_code};#{code}
258
+ def #{method_name}(local_assigns, output_buffer)
259
+ _old_virtual_path, @virtual_path = @virtual_path, #{@virtual_path.inspect};_old_output_buffer = @output_buffer;#{locals_code};#{code}
236
260
  ensure
237
- @_virtual_path, @output_buffer = _old_virtual_path, _old_output_buffer
261
+ @virtual_path, @output_buffer = _old_virtual_path, _old_output_buffer
238
262
  end
239
263
  end_src
240
264
 
@@ -257,8 +281,6 @@ module ActionView
257
281
  begin
258
282
  mod.module_eval(source, identifier, 0)
259
283
  ObjectSpace.define_finalizer(self, Finalizer[method_name, mod])
260
-
261
- method_name
262
284
  rescue Exception => e # errors from template code
263
285
  if logger = (view && view.logger)
264
286
  logger.debug "ERROR: compiling #{method_name} RAISED #{e}"
@@ -270,12 +292,27 @@ module ActionView
270
292
  end
271
293
  end
272
294
 
273
- def build_method_name(locals)
274
- @method_names[locals.keys.hash] ||= "_#{identifier_method_name}__#{@identifier.hash}_#{__id__}_#{locals.keys.hash}".gsub('-', "_")
295
+ def handle_render_error(view, e) #:nodoc:
296
+ if e.is_a?(Template::Error)
297
+ e.sub_template_of(self)
298
+ raise e
299
+ else
300
+ assigns = view.respond_to?(:assigns) ? view.assigns : {}
301
+ template = @virtual_path ? refresh(view) : self
302
+ raise Template::Error.new(template, assigns, e)
303
+ end
304
+ end
305
+
306
+ def locals_code #:nodoc:
307
+ @locals.map { |key| "#{key} = local_assigns[:#{key}];" }.join
308
+ end
309
+
310
+ def method_name #:nodoc:
311
+ @method_name ||= "_#{identifier_method_name}__#{@identifier.hash}_#{__id__}".gsub('-', "_")
275
312
  end
276
313
 
277
- def identifier_method_name
278
- @identifier_method_name ||= inspect.gsub(/[^a-z_]/, '_')
314
+ def identifier_method_name #:nodoc:
315
+ inspect.gsub(/[^a-z_]/, '_')
279
316
  end
280
317
  end
281
318
  end
@@ -27,7 +27,7 @@ module ActionView
27
27
  class MissingTemplate < ActionViewError #:nodoc:
28
28
  attr_reader :path
29
29
 
30
- def initialize(paths, path, details, partial)
30
+ def initialize(paths, path, prefixes, partial, details, *)
31
31
  @path = path
32
32
  display_paths = paths.compact.map{ |p| p.to_s.inspect }.join(", ")
33
33
  template_type = if partial
@@ -38,20 +38,27 @@ module ActionView
38
38
  'template'
39
39
  end
40
40
 
41
- super("Missing #{template_type} #{path} with #{details.inspect} in view paths #{display_paths}")
41
+ searched_paths = prefixes.map { |prefix| [prefix, path].join("/") }
42
+
43
+ out = "Missing #{template_type} #{searched_paths.join(", ")} with #{details.inspect}. Searched in:\n"
44
+ out += paths.compact.map { |p| " * #{p.to_s.inspect}\n" }.join
45
+ super out
42
46
  end
43
47
  end
44
48
 
45
49
  class Template
46
- # The Template::Error exception is raised when the compilation of the template fails. This exception then gathers a
47
- # bunch of intimate details and uses it to report a very precise exception message.
50
+ # The Template::Error exception is raised when the compilation or rendering of the template
51
+ # fails. This exception then gathers a bunch of intimate details and uses it to report a
52
+ # precise exception message.
48
53
  class Error < ActionViewError #:nodoc:
49
54
  SOURCE_CODE_RADIUS = 3
50
55
 
51
56
  attr_reader :original_exception, :backtrace
52
57
 
53
58
  def initialize(template, assigns, original_exception)
59
+ super(original_exception.message)
54
60
  @template, @assigns, @original_exception = template, assigns.dup, original_exception
61
+ @sub_templates = nil
55
62
  @backtrace = original_exception.backtrace
56
63
  end
57
64
 
@@ -59,10 +66,6 @@ module ActionView
59
66
  @template.identifier
60
67
  end
61
68
 
62
- def message
63
- ActiveSupport::Deprecation.silence { original_exception.message }
64
- end
65
-
66
69
  def sub_template_message
67
70
  if @sub_templates
68
71
  "Trace of template inclusion: " +