class Middleman::TemplateRenderer
def self.cache
def self.cache @_cache ||= Cache.new end
def self.locate_layout(app, name, preferred_engine=nil)
def self.locate_layout(app, name, preferred_engine=nil) resolve_opts = {} resolve_opts[:preferred_engine] = preferred_engine unless preferred_engine.nil? # Check layouts folder layout_file = resolve_template(app, File.join(app.config[:layouts_dir], name.to_s), resolve_opts) # If we didn't find it, check root layout_file = resolve_template(app, name, resolve_opts) unless layout_file # Return the path layout_file end
def self.resolve_template(app, request_path, options={})
def self.resolve_template(app, request_path, options={}) # Find the path by searching relative_path = Util.strip_leading_slash(request_path.to_s) # By default, any engine will do preferred_engines = [] # If we're specifically looking for a preferred engine if options.key?(:preferred_engine) extension_class = ::Middleman::Util.tilt_class(options[:preferred_engine]) # Get a list of extensions for a preferred engine preferred_engines += ::Tilt.default_mapping.extensions_for(extension_class) end preferred_engines << '*' preferred_engines << nil if options[:try_static] found_template = nil preferred_engines.each do |preferred_engine| path_with_ext = relative_path.dup path_with_ext << ('.' + preferred_engine) unless preferred_engine.nil? globbing = preferred_engine == '*' # Cache lookups in build mode only file = if app.build? cache.fetch(path_with_ext, preferred_engine) do app.files.find(:source, path_with_ext, globbing) end else app.files.find(:source, path_with_ext, globbing) end found_template = file if file && (preferred_engine.nil? || ::Middleman::Util.tilt_class(file[:full_path].to_s)) break if found_template end # If we found one, return it found_template end
def _render_with_all_renderers(path, locs, context, opts, &block)
def _render_with_all_renderers(path, locs, context, opts, &block) # Keep rendering template until we've used up all extensions. This # handles cases like `style.css.sass.erb` content = nil while ::Middleman::Util.tilt_class(path) begin opts[:template_body] = content if content content_renderer = ::Middleman::FileRenderer.new(@app, path) content = content_renderer.render(locs, opts, context, &block) path = File.basename(path, File.extname(path)) rescue LocalJumpError raise "Tried to render a layout (calls yield) at #{path} like it was a template. Non-default layouts need to be in #{@app.config[:source]}/#{@app.config[:layouts_dir]}." end end content end
def fetch_layout(engine, opts)
def fetch_layout(engine, opts) # The layout name comes from either the system default or the options local_layout = opts.key?(:layout) ? opts[:layout] : @app.config[:layout] return unless local_layout # Look for engine-specific options engine_options = @app.config.respond_to?(engine) ? @app.config.send(engine) : {} # The engine for the layout can be set in options, engine_options or passed # into this method layout_engine = if opts.key?(:layout_engine) opts[:layout_engine] elsif engine_options.key?(:layout_engine) engine_options[:layout_engine] else engine end # Automatic mode if local_layout == :_auto_layout # Look for :layout of any extension # If found, use it. If not, continue locate_layout(:layout, layout_engine) elsif layout_file = locate_layout(local_layout, layout_engine) # Look for specific layout # If found, use it. If not, error. layout_file else raise ::Middleman::TemplateRenderer::TemplateNotFound, "Could not locate layout: #{local_layout}" end end
def initialize(app, path)
def initialize(app, path) @app = app @path = path end
def locate_layout(name, preferred_engine=nil)
def locate_layout(name, preferred_engine=nil) self.class.locate_layout(@app, name, preferred_engine) end
def render(locs={}, opts={}, &block)
def render(locs={}, opts={}, &block) path = @path.dup locals = locs.dup.freeze options = opts.dup extension = File.extname(path) engine = extension[1..-1].to_sym if defined?(::I18n) old_locale = ::I18n.locale ::I18n.locale = options.delete(:locale) if options[:locale] # Backwards compat ::I18n.locale = options.delete(:lang) if options[:lang] end # Sandboxed class for template eval context = @app.template_context_class.new(@app, locals, options) # Add extension helpers to context. @app.extensions.add_exposed_to_context(context) locals.each do |k, _| next unless context.respond_to?(k) && ![:current_path, :paginate, :page_articles, :blog_controller, :lang, :locale, :data].include?(k.to_sym) msg = "Template local `#{k}` tried to overwrite an existing context value. Please rename the key when passing to `locals`" if @app.build? throw msg else @app.logger.error(msg) end end content = ::Middleman::Util.instrument 'builder.output.resource.render-template', path: File.basename(path) do _render_with_all_renderers(path, locals, context, options, &block) end # If we need a layout and have a layout, use it layout_file = fetch_layout(engine, options) if layout_file content = if layout_file = fetch_layout(engine, options) layout_renderer = ::Middleman::FileRenderer.new(@app, layout_file[:relative_path].to_s) ::Middleman::Util.instrument 'builder.output.resource.render-layout', path: File.basename(layout_file[:relative_path].to_s) do layout_renderer.render(locals, options, context) { content } end else content end end # Return result content ensure # Pop all the saved variables from earlier as we may be returning to a # previous render (layouts, partials, nested layouts). ::I18n.locale = old_locale if defined?(::I18n) end
def resolve_template(request_path, options={})
def resolve_template(request_path, options={}) self.class.resolve_template(@app, request_path, options) end