diff --git a/middleman-core/lib/middleman-core/config_context.rb b/middleman-core/lib/middleman-core/config_context.rb index ebf0d67c..3ee98632 100644 --- a/middleman-core/lib/middleman-core/config_context.rb +++ b/middleman-core/lib/middleman-core/config_context.rb @@ -23,7 +23,7 @@ module Middleman end def include(mod) - self.extend(mod) + extend(mod) end def helpers(*helper_modules, &block) diff --git a/middleman-core/lib/middleman-core/core_extensions/front_matter.rb b/middleman-core/lib/middleman-core/core_extensions/front_matter.rb index 49f4195f..0f69c634 100644 --- a/middleman-core/lib/middleman-core/core_extensions/front_matter.rb +++ b/middleman-core/lib/middleman-core/core_extensions/front_matter.rb @@ -73,18 +73,20 @@ module Middleman::CoreExtensions return [{}, nil] unless file - return @cache[file[:full_path]] if @cache.key?(file[:full_path]) + file_path = file[:full_path].to_s - @cache[file[:full_path]] = ::Middleman::Util::Data.parse( - file, - app.config[:frontmatter_delims] - ) + @cache[file_path] ||= begin + ::Middleman::Util::Data.parse( + file, + app.config[:frontmatter_delims] + ) + end end Contract ArrayOf[IsA['Middleman::SourceFile']], ArrayOf[IsA['Middleman::SourceFile']] => Any def clear_data(updated_files, removed_files) (updated_files + removed_files).each do |file| - @cache.delete(file[:full_path]) + @cache.delete(file[:full_path].to_s) end end end diff --git a/middleman-core/lib/middleman-core/core_extensions/inline_url_rewriter.rb b/middleman-core/lib/middleman-core/core_extensions/inline_url_rewriter.rb index 14cf4715..24d541fa 100644 --- a/middleman-core/lib/middleman-core/core_extensions/inline_url_rewriter.rb +++ b/middleman-core/lib/middleman-core/core_extensions/inline_url_rewriter.rb @@ -1,6 +1,6 @@ require 'rack' require 'rack/response' -require 'addressable/uri' +require 'memoist' require 'middleman-core/util' require 'middleman-core/contracts' @@ -33,6 +33,8 @@ module Middleman end def after_configuration + return if @rewriters.empty? + rewriters = @rewriters.values.sort do |a, b| if b[:after] && b[:after] == a[:id] 1 @@ -45,6 +47,7 @@ module Middleman end class Rack + extend Memoist include Contracts Contract RespondTo[:call], { @@ -55,6 +58,17 @@ module Middleman @rack_app = app @middleman_app = options.fetch(:middleman_app) @rewriters = options.fetch(:rewriters) + + all_source_exts = @rewriters + .reduce([]) { |sum, rewriter| sum + rewriter[:source_extensions] } + .flatten + .uniq + @source_exts_regex_text = Regexp.union(all_source_exts).to_s + + @all_asset_exts = @rewriters + .reduce([]) { |sum, rewriter| sum + rewriter[:url_extensions] } + .flatten + .uniq end def call(env) @@ -63,27 +77,16 @@ module Middleman # Allow configuration or upstream request to skip all rewriting return [status, headers, response] if env['bypass_inline_url_rewriter'] == 'true' - all_source_exts = @rewriters - .reduce([]) { |sum, rewriter| sum + rewriter[:source_extensions] } - .flatten - .uniq - source_exts_regex_text = Regexp.union(all_source_exts).to_s - - all_asset_exts = @rewriters - .reduce([]) { |sum, rewriter| sum + rewriter[:url_extensions] } - .flatten - .uniq - path = ::Middleman::Util.full_path(env['PATH_INFO'], @middleman_app) - return [status, headers, response] unless path =~ /(^\/$)|(#{source_exts_regex_text}$)/ + return [status, headers, response] unless path =~ /(^\/$)|(#{@source_exts_regex_text}$)/ return [status, headers, response] unless body = ::Middleman::Util.extract_response_text(response) dirpath = ::Pathname.new(File.dirname(path)) rewritten = ::Middleman::Util.instrument 'inline_url_rewriter', path: path do - ::Middleman::Util.rewrite_paths(body, path, all_asset_exts, @middleman_app) do |asset_path| - uri = ::Addressable::URI.parse(asset_path) + ::Middleman::Util.rewrite_paths(body, path, @all_asset_exts, @middleman_app) do |asset_path| + uri = ::Middleman::Util.parse_uri(asset_path) relative_path = uri.host.nil? @@ -144,6 +147,7 @@ module Middleman false end end + memoize :should_ignore? end end end diff --git a/middleman-core/lib/middleman-core/extension.rb b/middleman-core/lib/middleman-core/extension.rb index 8f342e69..9805455a 100644 --- a/middleman-core/lib/middleman-core/extension.rb +++ b/middleman-core/lib/middleman-core/extension.rb @@ -1,4 +1,5 @@ require 'forwardable' +require 'memoist' require 'active_support/core_ext/class/attribute' require 'middleman-core/configuration' require 'middleman-core/contracts' @@ -66,6 +67,8 @@ module Middleman # @see http://middlemanapp.com/advanced/custom/ Middleman Custom Extensions Documentation class Extension extend Forwardable + extend Memoist + include Contracts def_delegator :@app, :logger @@ -510,7 +513,7 @@ module Middleman self.class.exposed_to_config.each do |k, v| ::Middleman::CoreExtensions::Collections::StepContext.add_to_context(k) do |*args, &b| r = context.method(:"__original_#{v}").call(*args, &b) - self.descriptors << r if r.respond_to?(:execute_descriptor) + descriptors << r if r.respond_to?(:execute_descriptor) end end end diff --git a/middleman-core/lib/middleman-core/extensions/asset_hash.rb b/middleman-core/lib/middleman-core/extensions/asset_hash.rb index b38bab9f..b0b955f9 100644 --- a/middleman-core/lib/middleman-core/extensions/asset_hash.rb +++ b/middleman-core/lib/middleman-core/extensions/asset_hash.rb @@ -32,7 +32,7 @@ class Middleman::Extensions::AssetHash < ::Middleman::Extension Contract String, Or[String, Pathname], Any => Maybe[String] def rewrite_url(asset_path, dirpath, _request_path) - uri = ::Addressable::URI.parse(asset_path) + uri = ::Middleman::Util.parse_uri(asset_path) relative_path = !uri.path.start_with?('/') full_asset_path = if relative_path diff --git a/middleman-core/lib/middleman-core/extensions/asset_host.rb b/middleman-core/lib/middleman-core/extensions/asset_host.rb index f7c7e8fc..7cb83d07 100644 --- a/middleman-core/lib/middleman-core/extensions/asset_host.rb +++ b/middleman-core/lib/middleman-core/extensions/asset_host.rb @@ -20,7 +20,7 @@ class Middleman::Extensions::AssetHost < ::Middleman::Extension Contract String, Or[String, Pathname], Any => String def rewrite_url(asset_path, dirpath, _request_path) - uri = ::Addressable::URI.parse(asset_path) + uri = ::Middleman::Util.parse_uri(asset_path) relative_path = uri.path[0..0] != '/' full_asset_path = if relative_path @@ -37,4 +37,5 @@ class Middleman::Extensions::AssetHost < ::Middleman::Extension File.join(asset_prefix, full_asset_path) end + memoize :rewrite_url end diff --git a/middleman-core/lib/middleman-core/extensions/minify_css.rb b/middleman-core/lib/middleman-core/extensions/minify_css.rb index cc513b6c..067b25bf 100644 --- a/middleman-core/lib/middleman-core/extensions/minify_css.rb +++ b/middleman-core/lib/middleman-core/extensions/minify_css.rb @@ -1,3 +1,4 @@ +require 'memoist' require 'middleman-core/contracts' # Minify CSS Extension @@ -30,6 +31,7 @@ class Middleman::Extensions::MinifyCss < ::Middleman::Extension # Rack middleware to look for CSS and compress it class Rack + extend Memoist include Contracts INLINE_CSS_REGEX = /(]*>\s*(?:\/\*\*\/)?\s*<\/style>)/m @@ -82,8 +84,9 @@ class Middleman::Extensions::MinifyCss < ::Middleman::Extension # @param [String] path # @return [Boolean] def ignore?(path) - @ignore.any? { |ignore| Middleman::Util.path_match(ignore, path) } + @ignore.any? { |ignore| ::Middleman::Util.path_match(ignore, path) } end + memoize :ignore? # Whether this type of content can be minified # @param [String, nil] content_type @@ -91,6 +94,7 @@ class Middleman::Extensions::MinifyCss < ::Middleman::Extension def minifiable?(content_type) @content_types.include?(content_type) end + memoize :minifiable? # Whether this type of content contains inline content that can be minified # @param [String, nil] content_type @@ -98,6 +102,7 @@ class Middleman::Extensions::MinifyCss < ::Middleman::Extension def minifiable_inline?(content_type) @inline_content_types.include?(content_type) end + memoize :minifiable_inline? # Minify the content # @param [String] content @@ -105,6 +110,7 @@ class Middleman::Extensions::MinifyCss < ::Middleman::Extension def minify(content) @compressor.compress(content) end + memoize :minify # Detect and minify inline content # @param [String] content @@ -114,5 +120,6 @@ class Middleman::Extensions::MinifyCss < ::Middleman::Extension $1 + minify($2) + $3 end end + memoize :minify_inline end end diff --git a/middleman-core/lib/middleman-core/extensions/minify_javascript.rb b/middleman-core/lib/middleman-core/extensions/minify_javascript.rb index acd71224..48490470 100644 --- a/middleman-core/lib/middleman-core/extensions/minify_javascript.rb +++ b/middleman-core/lib/middleman-core/extensions/minify_javascript.rb @@ -1,4 +1,5 @@ require 'middleman-core/contracts' +require 'memoist' # Minify Javascript Extension class Middleman::Extensions::MinifyJavascript < ::Middleman::Extension @@ -22,6 +23,7 @@ class Middleman::Extensions::MinifyJavascript < ::Middleman::Extension # Rack middleware to look for JS and compress it class Rack + extend Memoist include Contracts INLINE_JS_REGEX = /(]*>\s*(?:\/\/(?:(?:)|(?:\]\]>)))?\s*<\/script>)/m @@ -76,6 +78,7 @@ class Middleman::Extensions::MinifyJavascript < ::Middleman::Extension def ignore?(path) @ignore.any? { |ignore| Middleman::Util.path_match(ignore, path) } end + memoize :ignore? # Whether this type of content can be minified # @param [String, nil] content_type @@ -83,6 +86,7 @@ class Middleman::Extensions::MinifyJavascript < ::Middleman::Extension def minifiable?(content_type) @content_types.include?(content_type) end + memoize :minifiable? # Whether this type of content contains inline content that can be minified # @param [String, nil] content_type @@ -90,6 +94,7 @@ class Middleman::Extensions::MinifyJavascript < ::Middleman::Extension def minifiable_inline?(content_type) @inline_content_types.include?(content_type) end + memoize :minifiable_inline? # Minify the content # @param [String] content @@ -100,6 +105,7 @@ class Middleman::Extensions::MinifyJavascript < ::Middleman::Extension warn "WARNING: Couldn't compress JavaScript in #{@path}: #{e.message}" content end + memoize :minify # Detect and minify inline content # @param [String] content @@ -119,5 +125,6 @@ class Middleman::Extensions::MinifyJavascript < ::Middleman::Extension end end end + memoize :minify_inline end end diff --git a/middleman-core/lib/middleman-core/extensions/relative_assets.rb b/middleman-core/lib/middleman-core/extensions/relative_assets.rb index b319de1b..33ac420e 100644 --- a/middleman-core/lib/middleman-core/extensions/relative_assets.rb +++ b/middleman-core/lib/middleman-core/extensions/relative_assets.rb @@ -33,7 +33,7 @@ class Middleman::Extensions::RelativeAssets < ::Middleman::Extension Contract String, Or[String, Pathname], Any => Maybe[String] def rewrite_url(asset_path, dirpath, request_path) - uri = ::Addressable::URI.parse(asset_path) + uri = ::Middleman::Util.parse_uri(asset_path) return if uri.path[0..0] != '/' @@ -50,4 +50,5 @@ class Middleman::Extensions::RelativeAssets < ::Middleman::Extension result end + memoize :rewrite_url end diff --git a/middleman-core/lib/middleman-core/file_renderer.rb b/middleman-core/lib/middleman-core/file_renderer.rb index ae1212b4..a3e8b193 100644 --- a/middleman-core/lib/middleman-core/file_renderer.rb +++ b/middleman-core/lib/middleman-core/file_renderer.rb @@ -59,7 +59,7 @@ module Middleman # Overwrite with frontmatter options options = options.deep_merge(options[:renderer_options]) if options[:renderer_options] - template_class = ::Tilt[path] + template_class = ::Middleman::Util.tilt_class(path) # Allow hooks to manipulate the template before render body = @app.callbacks_for(:before_render).reduce(body) do |sum, callback| @@ -99,12 +99,12 @@ module Middleman def template_data_for_file file = @app.files.find(:source, @path) - if @app.extensions[:front_matter] || (file && !file[:types].include?(:no_frontmatter)) + if @app.extensions[:front_matter] && (file && !file[:types].include?(:no_frontmatter)) result = @app.extensions[:front_matter].template_data_for_file(@path) return result unless result.nil? end - file ? file.read : File.read(@path) + file ? file.read : ::File.read(@path) end protected @@ -122,7 +122,7 @@ module Middleman # Find all the engines which handle this extension in tilt. Look for # config variables of that name and merge it - extension_class = ::Tilt[ext] + extension_class = ::Middleman::Util.tilt_class(ext) ::Tilt.mappings.each do |mapping_ext, engines| next unless engines.include? extension_class engine_options = @app.config[mapping_ext.to_sym] || {} diff --git a/middleman-core/lib/middleman-core/sitemap/resource.rb b/middleman-core/lib/middleman-core/sitemap/resource.rb index fd236f3f..7c18078f 100644 --- a/middleman-core/lib/middleman-core/sitemap/resource.rb +++ b/middleman-core/lib/middleman-core/sitemap/resource.rb @@ -75,7 +75,7 @@ module Middleman Contract Bool def template? return false if file_descriptor.nil? - !::Tilt[file_descriptor[:full_path].to_s].nil? + !::Middleman::Util.tilt_class(file_descriptor[:full_path].to_s).nil? end # Backwards compatible method for turning descriptor into a string. diff --git a/middleman-core/lib/middleman-core/sources/source_watcher.rb b/middleman-core/lib/middleman-core/sources/source_watcher.rb index 2c034570..9852e223 100644 --- a/middleman-core/lib/middleman-core/sources/source_watcher.rb +++ b/middleman-core/lib/middleman-core/sources/source_watcher.rb @@ -298,9 +298,15 @@ module Middleman relative_path = path.relative_path_from(directory) relative_path = File.join(destination_dir, relative_path) if destination_dir + types << :no_frontmatter if partial?(relative_path.to_s) + ::Middleman::SourceFile.new(Pathname(relative_path), path, directory, types, 0) end + def partial?(relative_path) + relative_path.split(::File::SEPARATOR).any? { |p| p.start_with?('_') } + end + Contract IsA['Middleman::SourceFile'] => Any def record_file_change(f) if @files[f[:full_path]] diff --git a/middleman-core/lib/middleman-core/template_context.rb b/middleman-core/lib/middleman-core/template_context.rb index 99ec6ee5..ca5f3632 100644 --- a/middleman-core/lib/middleman-core/template_context.rb +++ b/middleman-core/lib/middleman-core/template_context.rb @@ -84,6 +84,7 @@ module Middleman # Reset stored buffer, regardless of success restore_buffer(buf_was) end + # Render the layout, with the contents of the block inside. concat_safe_content render_file(layout_file, @locs, @opts) { content } ensure @@ -185,7 +186,7 @@ module Middleman # handles cases like `style.css.sass.erb` content = nil - while ::Tilt[path] + while ::Middleman::Util.tilt_class(path) begin opts[:template_body] = content if content diff --git a/middleman-core/lib/middleman-core/template_renderer.rb b/middleman-core/lib/middleman-core/template_renderer.rb index 1cf9febf..0dcf2af8 100644 --- a/middleman-core/lib/middleman-core/template_renderer.rb +++ b/middleman-core/lib/middleman-core/template_renderer.rb @@ -61,7 +61,7 @@ module Middleman # If we're specifically looking for a preferred engine if options.key?(:preferred_engine) - extension_class = ::Tilt[options[:preferred_engine]] + extension_class = ::Middleman::Util.tilt_class(options[:preferred_engine]) # Get a list of extensions for a preferred engine preferred_engines += ::Tilt.mappings.select do |_, engines| @@ -89,7 +89,7 @@ module Middleman app.files.find(:source, path_with_ext, globbing) end - found_template = file if file && (preferred_engine.nil? || ::Tilt[file[:full_path]]) + found_template = file if file && (preferred_engine.nil? || ::Middleman::Util.tilt_class(file[:full_path].to_s)) break if found_template end @@ -134,9 +134,9 @@ module Middleman @app.extensions.add_exposed_to_context(context) locals.each do |k, _| - next unless context.respond_to?(k) && k != :current_path + next unless context.respond_to?(k) && ![:current_path, :paginate, :page_articles, :blog_controller, :lang, :locale].include?(k.to_sym) - msg = "Template local `#{k}` tried to overwrite an existing context value. Please renamed the key when passing to `locals`" + msg = "Template local `#{k}` tried to overwrite an existing context value. Please rename the key when passing to `locals`" if @app.build? throw msg @@ -152,13 +152,14 @@ module Middleman # If we need a layout and have a layout, use it layout_file = fetch_layout(engine, options) if layout_file - content = ::Middleman::Util.instrument 'builder.output.resource.render-layout', path: File.basename(layout_file[:relative_path].to_s) do - if layout_file = fetch_layout(engine, options) - layout_renderer = ::Middleman::FileRenderer.new(@app, layout_file[:relative_path].to_s) + 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 } - else - content end + else + content end end @@ -177,7 +178,7 @@ module Middleman # handles cases like `style.css.sass.erb` content = nil - while ::Tilt[path] + while ::Middleman::Util.tilt_class(path) begin opts[:template_body] = content if content diff --git a/middleman-core/lib/middleman-core/util/data.rb b/middleman-core/lib/middleman-core/util/data.rb index 1a521b8d..ce73540a 100644 --- a/middleman-core/lib/middleman-core/util/data.rb +++ b/middleman-core/lib/middleman-core/util/data.rb @@ -3,6 +3,7 @@ require 'json' require 'pathname' require 'backports/2.1.0/array/to_h' require 'hashie' +require 'memoist' require 'middleman-core/util/binary' require 'middleman-core/contracts' @@ -36,6 +37,7 @@ module Middleman end module Data + extend Memoist include Contracts module_function @@ -55,20 +57,7 @@ module Middleman return [{}, nil] end - start_delims, stop_delims = frontmatter_delims - .values - .flatten(1) - .transpose - .map(&::Regexp.method(:union)) - - match = / - \A(?:[^\r\n]*coding:[^\r\n]*\r?\n)? - (?#{start_delims})[ ]*\r?\n - (?.*?)[ ]*\r?\n? - ^(?#{stop_delims})[ ]*\r?\n? - \r?\n? - (?.*) - /mx.match(content) || {} + match = build_regex(frontmatter_delims).match(content) || {} unless match[:frontmatter] case known_type @@ -98,27 +87,53 @@ module Middleman end end + def build_regex(frontmatter_delims) + start_delims, stop_delims = frontmatter_delims + .values + .flatten(1) + .transpose + .map(&::Regexp.method(:union)) + + match = / + \A(?:[^\r\n]*coding:[^\r\n]*\r?\n)? + (?#{start_delims})[ ]*\r?\n + (?.*?)[ ]*\r?\n? + ^(?#{stop_delims})[ ]*\r?\n? + \r?\n? + (?.*) + /mx + end + memoize :build_regex + # Parse YAML frontmatter out of a string # @param [String] content # @return [Hash] - Contract String, Pathname, Bool => Hash + Contract String, Pathname => Hash def parse_yaml(content, full_path) - symbolize_recursive(::YAML.load(content) || {}) + c = ::Middleman::Util.instrument 'parse.yaml' do + ::YAML.load(content) + end + c ? symbolize_recursive(c) : {} rescue StandardError, ::Psych::SyntaxError => error warn "YAML Exception parsing #{full_path}: #{error.message}" {} end + memoize :parse_yaml # Parse JSON frontmatter out of a string # @param [String] content # @return [Hash] Contract String, Pathname => Hash def parse_json(content, full_path) - symbolize_recursive(::JSON.parse(content) || {}) + c = ::Middleman::Util.instrument 'parse.json' do + ::JSON.parse(content) + end + c ? symbolize_recursive(c) : {} rescue StandardError => error warn "JSON Exception parsing #{full_path}: #{error.message}" {} end + memoize :parse_json def symbolize_recursive(value) case value diff --git a/middleman-core/lib/middleman-core/util/files.rb b/middleman-core/lib/middleman-core/util/files.rb index 9ba716f3..e57179a4 100644 --- a/middleman-core/lib/middleman-core/util/files.rb +++ b/middleman-core/lib/middleman-core/util/files.rb @@ -54,15 +54,9 @@ module Middleman result.encode('UTF-8', 'UTF-8-MAC') end - Contract String => Bool - def tilt_recognizes?(path) - @@tilt_lookup_cache ||= {} - @@tilt_lookup_cache[path] ||= ::Tilt[path] - end - Contract String => String def step_through_extensions(path) - while tilt_recognizes?(path) + while ::Middleman::Util.tilt_class(path) ext = ::File.extname(path) yield ext if block_given? diff --git a/middleman-core/lib/middleman-core/util/paths.rb b/middleman-core/lib/middleman-core/util/paths.rb index 97abf218..fa25e818 100644 --- a/middleman-core/lib/middleman-core/util/paths.rb +++ b/middleman-core/lib/middleman-core/util/paths.rb @@ -1,16 +1,32 @@ # Core Pathname library used for traversal require 'pathname' require 'uri' +require 'memoist' +require 'addressable' +require 'tilt' require 'middleman-core/contracts' # rubocop:disable ModuleLength module Middleman module Util + extend Memoist include Contracts module_function + Contract String => ::Addressable::URI + def parse_uri(uri) + ::Addressable::URI.parse(uri) + end + memoize :parse_uri + + Contract String => Any + def tilt_class(path) + ::Tilt[path] + end + memoize :tilt_class + # Normalize a path to not include a leading slash # @param [String] path # @return [String] @@ -19,6 +35,7 @@ module Middleman # The tr call works around a bug in Ruby's Unicode handling ::URI.decode(path).sub(%r{^/}, '').tr('', '') end + memoize :normalize_path # This is a separate method from normalize_path in case we # change how we normalize paths @@ -26,6 +43,7 @@ module Middleman def strip_leading_slash(path) path.sub(%r{^/}, '') end + memoize :strip_leading_slash # Get the path of a file of a given type # @@ -256,5 +274,6 @@ module Middleman ::File.fnmatch(matcher.to_s, path) end end + memoize :path_match end end diff --git a/middleman-core/lib/middleman-core/util/rack.rb b/middleman-core/lib/middleman-core/util/rack.rb index 0aeda851..2459052b 100644 --- a/middleman-core/lib/middleman-core/util/rack.rb +++ b/middleman-core/lib/middleman-core/util/rack.rb @@ -38,7 +38,7 @@ module Middleman current_resource = app.sitemap.find_resource_by_destination_path(path) begin - uri = ::Addressable::URI.parse(asset_path) + uri = ::Middleman::Util.parse_uri(asset_path) if uri.relative? && uri.host.nil? && !(asset_path =~ /^[^\/].*[a-z]+\.[a-z]+\/.*/) dest_path = ::Middleman::Util.url_for(app, asset_path, relative: false, current_resource: current_resource) diff --git a/middleman-core/middleman-core.gemspec b/middleman-core/middleman-core.gemspec index a589eb56..acea6a07 100644 --- a/middleman-core/middleman-core.gemspec +++ b/middleman-core/middleman-core.gemspec @@ -31,6 +31,7 @@ Gem::Specification.new do |s| s.add_dependency('activesupport', ['~> 4.2']) s.add_dependency('padrino-helpers', ['~> 0.13.0']) s.add_dependency("addressable", ["~> 2.3"]) + s.add_dependency('memoist', ['~> 0.14']) # Watcher s.add_dependency('listen', ['~> 3.0'])