lib/roger/template.rb in roger-1.3.5 vs lib/roger/template.rb in roger-1.4.0

- old
+ new

@@ -1,10 +1,12 @@ require "tilt" require "mime/types" require "yaml" require "ostruct" +require File.dirname(__FILE__) + "/template/template_context" + # We're enforcing Encoding to UTF-8 Encoding.default_external = "UTF-8" module Roger # Roger template processing class @@ -24,10 +26,21 @@ class << self def open(path, options = {}) fail "Unknown file #{path}" unless File.exist?(path) new(File.read(path), options.update(source_path: path)) end + + # Register a helper module that should be included in + # every template context. + def helper(mod) + @helpers ||= [] + @helpers << mod + end + + def helpers + @helpers || [] + end end # @option options [String,Pathname] :source_path The path to # the source of the template being processed # @option options [String,Pathname] :layouts_path The path to where all layouts reside @@ -41,11 +54,11 @@ initialize_layout end def render(env = {}) - context = TemplateContext.new(self, env) + context = prepare_context(env) if @layout_template content_for_layout = template.render(context, {}) # yields @layout_template.render(context, {}) do |content_for| @@ -109,10 +122,21 @@ mime.to_s if mime end protected + def prepare_context(env) + context = TemplateContext.new(self, env) + + # Extend context with all helpers + self.class.helpers.each do |mod| + context.extend(mod) + end + + context + end + def initialize_layout return unless data[:layout] layout_template_path = find_template(data[:layout], :layouts_path) @layout_template = Tilt.new(layout_template_path.to_s) if layout_template_path @@ -156,98 +180,8 @@ end [data, source] rescue [{}, source] - end - end - - # The context that is passed to all templates - class TemplateContext - attr_accessor :_content_for_blocks - - def initialize(template, env = {}) - @_content_for_blocks = {} - @_template = template - @_env = env - - # Block counter to make sure erbtemp binding is always unique - @block_counter = 0 - end - - # The current Roger::Template in use - def template - @_template - end - - # Access to the front-matter of the document (if any) - def document - @_data ||= OpenStruct.new(template.data) - end - - # The current environment variables. - def env - @_env - end - - # Capture content in blocks in the template for later use in the layout. - # Currently only works in ERB templates. Use like this in the template: - # - # ``` - # <% content_for :name %> bla bla <% end %> - # ``` - # - # Place it like this in the layout: - # - # ``` - # <%= yield :name %> - # ``` - def content_for(block_name, &block) - @_content_for_blocks[block_name] = capture(&block) - end - - # rubocop:disable Lint/Eval - def capture(&block) - unless template.template.is_a?(Tilt::ERBTemplate) - fail ArgumentError, "content_for works only with ERB Templates" - end - - @block_counter += 1 - counter = @block_counter - - eval "@_erbout_tmp#{counter} = _erbout", block.binding - eval "_erbout = \"\"", block.binding - t = Tilt::ERBTemplate.new { "<%= yield %>" } - t.render(&block) - ensure - eval "_erbout = @_erbout_tmp#{counter}", block.binding - end - - def partial(name, options = {}, &block) - template_path = template.find_template(name, :partials_path) - if template_path - out = render_partial(template_path, options, &block) - if block_given? - eval "_erbout.concat(#{out.dump})", block.binding - else - out - end - else - fail ArgumentError, "No such partial #{name}, referenced from #{template.source_path}" - end - end - # rubocop:enable Lint/Eval - - protected - - # Capture a block and render the partial - def render_partial(template_path, options, &block) - partial_template = Tilt.new(template_path.to_s) - if block_given? - block_content = capture(&block) - else - block_content = "" - end - partial_template.render(self, options[:locals] || {}) { block_content } end end end