diff --git a/lib/middleman/base.rb b/lib/middleman/base.rb index 55125a22..25e9fa21 100644 --- a/lib/middleman/base.rb +++ b/lib/middleman/base.rb @@ -176,15 +176,15 @@ class Middleman::Base # Add Guard Callbacks register Middleman::CoreExtensions::FileWatcher - # Sitemap - register Middleman::CoreExtensions::Sitemap - # Activate Data package register Middleman::CoreExtensions::Data # Setup custom rendering register Middleman::CoreExtensions::Rendering + # Sitemap + register Middleman::CoreExtensions::Sitemap + # Compass framework register Middleman::CoreExtensions::Compass @@ -231,6 +231,8 @@ class Middleman::Base # Current path defaults to nil, used in views. @current_path = nil + cache.clear + # Setup the default values from calls to set before initialization self.class.superclass.defaults.each { |k,v| set k,v } @@ -250,10 +252,18 @@ class Middleman::Base # # @private # @return [Middleman::Cache] The cache - def cache + def self.cache @_cache ||= ::Middleman::Cache.new end + # Cache accessor for instance, simply forwards to class + # + # @private + # @return [Middleman::Cache] The cache + def cache + self.class.cache + end + # Rack env attr :env @@ -334,7 +344,7 @@ class Middleman::Base # Valid content is a 200 status res.status = 200 - rescue ::Middleman::Sitemap::TemplateNotFound => e + rescue Middleman::CoreExtensions::Rendering::TemplateNotFound => e res.write "Error: #{e.message}" res.status = 500 end @@ -372,54 +382,6 @@ class Middleman::Base end end - # Sinatra/Padrino render method signature. Simply forwards to the sitemap - # - # @param [Symbol] Engine name - # @param [String] Path - # @param [Hash] Rendering options - # @param [Hash] Rendering locals - # @return [String] Output - def render(engine, data, options={}, locals={}, &block) - data = data.to_s - - found_partial = false - engine = nil - - if sitemap.exists?(current_path) - page = sitemap.page(current_path) - current_dir = File.dirname(page.source_file) - engine = File.extname(page.source_file)[1..-1].to_sym - - if current_dir != self.source_dir - relative_dir = File.join(current_dir.sub("#{self.source_dir}/", ""), data) - - found_partial, found_engine = Middleman::Sitemap::Template.resolve_template(self, relative_dir, :preferred_engine => engine) - - if !found_partial - found_partial, found_engine = Middleman::Sitemap::Template.resolve_template(self, relative_dir) - end - end - end - - if !found_partial && !engine.nil? - found_partial, found_engine = Middleman::Sitemap::Template.resolve_template(self, data, :preferred_engine => engine) - end - - if !found_partial - found_partial, found_engine = Middleman::Sitemap::Template.resolve_template(self, data) - end - - if found_partial - body = cache.fetch(:raw_template, found_partial) do - File.read(found_partial) - end - - Middleman::Sitemap::Template.static_render(self, found_partial, body, locals, options, &block) - else - throw "Could not find file to render: #{data}" - end - end - # Add a new mime-type for a specific extension # # @param [Symbol] File extension diff --git a/lib/middleman/core_extensions/rendering.rb b/lib/middleman/core_extensions/rendering.rb index 7b8a2c54..99cae623 100644 --- a/lib/middleman/core_extensions/rendering.rb +++ b/lib/middleman/core_extensions/rendering.rb @@ -9,6 +9,8 @@ module Middleman::CoreExtensions::Rendering rescue LoadError end + app.send :include, InstanceMethods + # Activate custom renderers app.register Middleman::Renderers::Sass app.register Middleman::Renderers::Markdown @@ -17,4 +19,191 @@ module Middleman::CoreExtensions::Rendering end alias :included :registered end + + class TemplateNotFound < RuntimeError + end + + module InstanceMethods + def render_template(path, locs={}, opts={}) + content = render_individual_file(path, locs, opts) + + extension = File.extname(path) + needs_layout = !%w(.js .css .txt).include?(extension) + engine = extension[1..-1].to_sym + + if needs_layout && layout_path = fetch_layout(engine, opts) + content = render_individual_file(layout_path, locs, opts) { content } + end + + content + end + + # Sinatra/Padrino render method signature. + def render(engine, data, options={}, locals={}, &block) + data = data.to_s + + found_partial = false + engine = nil + + if sitemap.exists?(current_path) + page = sitemap.page(current_path) + current_dir = File.dirname(page.source_file) + engine = File.extname(page.source_file)[1..-1].to_sym + + if current_dir != self.source_dir + relative_dir = File.join(current_dir.sub("#{self.source_dir}/", ""), data) + + found_partial, found_engine = resolve_template(relative_dir, :preferred_engine => engine) + + if !found_partial + found_partial, found_engine = resolve_template(relative_dir) + end + end + end + + if !found_partial && !engine.nil? + found_partial, found_engine = resolve_template(data, :preferred_engine => engine) + end + + if !found_partial + found_partial, found_engine = resolve_template(data) + end + + if found_partial + render_individual_file(found_partial, locals, options, &block) + else + raise ::Middleman::CoreExtensions::Rendering::TemplateNotFound, "Could not locate partial: #{data}" + end + end + + # @private + def render_individual_file(path, locs = {}, opts = {}, &block) + path = path.to_s + body = cache.fetch(:raw_template, path) do + File.read(path) + end + + options = opts.merge(options_for_ext(File.extname(path))) + + template = cache.fetch(:compiled_template, options, body) do + ::Tilt.new(path, 1, options) { body } + end + + template.render(self, locs, &block) + end + + # @private + def options_for_ext(ext) + cache.fetch(:options_for_ext, ext) do + options = {} + + extension_class = ::Tilt[ext] + ::Tilt.mappings.each do |ext, engines| + next unless engines.include? extension_class + engine_options = respond_to?(ext.to_sym) ? send(ext.to_sym) : {} + options.merge!(engine_options) + end + + options + end + end + + # @private + def fetch_layout(engine, opts) + local_layout = opts.has_key?(:layout) ? opts[:layout] : layout + return false unless local_layout + + engine_options = respond_to?(engine) ? send(engine) : {} + + layout_engine = if opts.has_key?(:layout_engine) + opts[:layout_engine] + elsif engine_options.has_key?(:layout_engine) + engine_options[:layout_engine] + else + engine + end + + # Automatic + if local_layout == :_auto_layout + # Look for :layout of any extension + # If found, use it. If not, continue + locate_layout(:layout, layout_engine) || false + else + # Look for specific layout + # If found, use it. If not, error. + if layout_path = locate_layout(local_layout, layout_engine) + layout_path + else + raise ::Middleman::CoreExtensions::Rendering::TemplateNotFound, "Could not locate layout: #{local_layout}" + end + end + end + + # @private + def locate_layout(name, preferred_engine=nil) + layout_path = false + + if !preferred_engine.nil? + # Check root + layout_path, layout_engine = resolve_template(name, :preferred_engine => preferred_engine) + + # Check layouts folder + if !layout_path + layout_path, layout_engine = resolve_template(File.join("layouts", name.to_s), :preferred_engine => preferred_engine) + end + end + + # Check root, no preference + if !layout_path + layout_path, layout_engine = resolve_template(name) + end + + # Check layouts folder, no preference + if !layout_path + layout_path, layout_engine = resolve_template(File.join("layouts", name.to_s)) + end + + layout_path + end + + # @private + def resolve_template(request_path, options={}) + request_path = request_path.to_s + cache.fetch(:resolve_template, request_path, options) do + relative_path = request_path.sub(%r{^/}, "") + on_disk_path = File.expand_path(relative_path, self.source_dir) + + preferred_engine = "*" + + if options.has_key?(:preferred_engine) + extension_class = ::Tilt[options[:preferred_engine]] + matched_exts = [] + + # TODO: Cache this + ::Tilt.mappings.each do |ext, engines| + next unless engines.include? extension_class + matched_exts << ext + end + + if matched_exts.length > 0 + preferred_engine = "{" + matched_exts.join(",") + "}" + else + return false + end + end + + path_with_ext = on_disk_path + "." + preferred_engine + found_path = Dir[path_with_ext].find do |path| + ::Tilt[path] + end + + if found_path || File.exists?(on_disk_path) + engine = found_path ? File.extname(found_path)[1..-1].to_sym : nil + [ found_path || on_disk_path, engine ] + else + false + end + end + end + end end \ No newline at end of file diff --git a/lib/middleman/sitemap/template.rb b/lib/middleman/sitemap/template.rb index 7e500b12..bad7704b 100644 --- a/lib/middleman/sitemap/template.rb +++ b/lib/middleman/sitemap/template.rb @@ -1,7 +1,5 @@ module Middleman::Sitemap - class TemplateNotFound < RuntimeError - end - + class Template attr_accessor :page, :options, :locals, :blocks#, :dependencies @@ -53,14 +51,7 @@ module Middleman::Sitemap end app.instance_eval(&block) if block_given? - - content = internal_render(source_file, locs, opts) - - if layout_path = fetch_layout(opts) - content = internal_render(layout_path, locs, opts) { content } - end - - content + app.render_template(source_file, locs, opts) end protected @@ -71,136 +62,5 @@ module Middleman::Sitemap def cache self.class.cache end - - def self.options_for_ext(ext) - cache.fetch(:options_for_ext, ext) do - options = {} - - extension_class = ::Tilt[ext] - ::Tilt.mappings.each do |ext, engines| - next unless engines.include? extension_class - engine_options = respond_to?(ext.to_sym) ? send(ext.to_sym) : {} - options.merge!(engine_options) - end - - options - end - end - - def fetch_layout(opts) - return false if %w(.js .css .txt).include?(ext) - local_layout = opts.has_key?(:layout) ? opts[:layout] : app.layout - return false unless local_layout - - engine = File.extname(source_file)[1..-1].to_sym - engine_options = app.respond_to?(engine) ? app.send(engine) : {} - - layout_engine = if opts.has_key?(:layout_engine) - opts[:layout_engine] - elsif engine_options.has_key?(:layout_engine) - engine_options[:layout_engine] - else - engine - end - - # Automatic - if local_layout == :_auto_layout - # Look for :layout of any extension - # If found, use it. If not, continue - locate_layout(:layout, layout_engine) || false - else - # Look for specific layout - # If found, use it. If not, error. - if layout_path = locate_layout(local_layout, layout_engine) - layout_path - else - raise Middleman::Sitemap::TemplateNotFound, "Could not locate layout: #{local_layout}" - end - end - end - - def locate_layout(name, preferred_engine=nil) - layout_path = false - - if !preferred_engine.nil? - # Check root - layout_path, *etc = self.class.resolve_template(app, name, :preferred_engine => preferred_engine) - - # Check layouts folder - if !layout_path - layout_path, *etc = self.class.resolve_template(app, File.join("layouts", name.to_s), :preferred_engine => preferred_engine) - end - end - - # Check root, no preference - if !layout_path - layout_path, *etc = self.class.resolve_template(app, name) - end - - # Check layouts folder, no preference - if !layout_path - layout_path, *etc = self.class.resolve_template(app, File.join("layouts", name.to_s)) - end - - layout_path - end - - def self.resolve_template(app, request_path, options={}) - request_path = request_path.to_s - cache.fetch(:resolve_template, request_path, options) do - relative_path = request_path.sub(%r{^/}, "") - on_disk_path = File.expand_path(relative_path, app.source_dir) - - preferred_engine = "*" - - if options.has_key?(:preferred_engine) - extension_class = ::Tilt[options[:preferred_engine]] - matched_exts = [] - - # TODO: Cache this - ::Tilt.mappings.each do |ext, engines| - next unless engines.include? extension_class - matched_exts << ext - end - - if matched_exts.length > 0 - preferred_engine = "{" + matched_exts.join(",") + "}" - else - return false - end - end - - path_with_ext = on_disk_path + "." + preferred_engine - found_path = Dir[path_with_ext].find do |path| - ::Tilt[path] - end - - if found_path || File.exists?(on_disk_path) - engine = found_path ? File.extname(found_path)[1..-1].to_sym : nil - [ found_path || on_disk_path, engine ] - else - false - end - end - end - - def internal_render(path, locs = {}, opts = {}, &block) - path = path.to_s - body = app.cache.fetch(:raw_template, path) do - File.read(path) - end - - self.class.static_render(app, path, body, locs, opts, &block) - end - - def self.static_render(app, path, body, locs = {}, opts = {}, &block) - options = opts.merge(options_for_ext(File.extname(path))) - - template = cache.fetch(:compiled_template, options, body) do - ::Tilt.new(path, 1, options) { body } - end - - template.render(app, locs, &block) - end end end \ No newline at end of file