aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib/action_view/renderable.rb
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/lib/action_view/renderable.rb')
-rw-r--r--actionpack/lib/action_view/renderable.rb81
1 files changed, 61 insertions, 20 deletions
diff --git a/actionpack/lib/action_view/renderable.rb b/actionpack/lib/action_view/renderable.rb
index 6a8a0e44fc..2c4302146f 100644
--- a/actionpack/lib/action_view/renderable.rb
+++ b/actionpack/lib/action_view/renderable.rb
@@ -1,37 +1,78 @@
module ActionView
module Renderable
- # TODO: Local assigns should not be tied to template instance
- attr_accessor :locals
+ # NOTE: The template that this mixin is beening include into is frozen
+ # So you can not set or modify any instance variables
- # TODO: These readers should be private
- attr_reader :filename, :source, :handler
+ def self.included(base)
+ @@mutex = Mutex.new
+ end
+
+ # NOTE: Exception to earlier notice. Ensure this is called before freeze
+ def handler
+ @handler ||= Template.handler_class_for_extension(extension)
+ end
- def render
- prepare!
- @handler.render(self, @locals)
+ # NOTE: Exception to earlier notice. Ensure this is called before freeze
+ def compiled_source
+ @compiled_source ||= handler.new(nil).compile(self) if handler.compilable?
end
- def method
- ['_run', @extension, @method_segment, local_assigns_keys].compact.join('_').to_sym
+ def render(view, local_assigns = {})
+ view.first_render ||= self
+ view.send(:evaluate_assigns)
+ view.current_render_extension = extension
+ compile(local_assigns) if handler.compilable?
+ handler.new(view).render(self, local_assigns)
+ end
+
+ def method(local_assigns)
+ if local_assigns && local_assigns.any?
+ local_assigns_keys = "locals_#{local_assigns.keys.map { |k| k.to_s }.sort.join('_')}"
+ end
+ ['_run', extension, method_segment, local_assigns_keys].compact.join('_').to_sym
end
private
- def prepare!
- unless @prepared
- @view.send(:evaluate_assigns)
- @view.current_render_extension = @extension
+ # Compile and evaluate the template's code
+ def compile(local_assigns)
+ render_symbol = method(local_assigns)
- if @handler.compilable?
- @handler.compile_template(self) # compile the given template, if necessary
- end
+ @@mutex.synchronize do
+ return false unless recompile?(render_symbol)
+
+ locals_code = local_assigns.keys.map { |key| "#{key} = local_assigns[:#{key}];" }.join
+
+ source = <<-end_src
+ def #{render_symbol}(local_assigns)
+ old_output_buffer = output_buffer;#{locals_code};#{compiled_source}
+ ensure
+ self.output_buffer = old_output_buffer
+ end
+ end_src
- @prepared = true
+ begin
+ file_name = respond_to?(:filename) ? filename : 'compiled-template'
+ ActionView::Base::CompiledTemplates.module_eval(source, file_name, 0)
+ rescue Exception => e # errors from template code
+ if logger = ActionController::Base.logger
+ logger.debug "ERROR: compiling #{render_symbol} RAISED #{e}"
+ logger.debug "Function body: #{source}"
+ logger.debug "Backtrace: #{e.backtrace.join("\n")}"
+ end
+
+ raise ActionView::TemplateError.new(self, {}, e)
+ end
end
end
- def local_assigns_keys
- if @locals && @locals.any?
- "locals_#{@locals.keys.map { |k| k.to_s }.sort.join('_')}"
+ # Method to check whether template compilation is necessary.
+ # The template will be compiled if the file has not been compiled yet, or
+ # if local_assigns has a new key, which isn't supported by the compiled code yet.
+ def recompile?(symbol)
+ unless Base::CompiledTemplates.instance_methods.include?(symbol) && Base.cache_template_loading
+ true
+ else
+ false
end
end
end