From bf19bbfed7a13f0131e49f36916737e501c40335 Mon Sep 17 00:00:00 2001 From: Thomas Reynolds Date: Wed, 13 Jan 2016 17:16:36 -0800 Subject: [PATCH] Perf --- middleman-cli/lib/middleman-cli/build.rb | 49 +++--- middleman-core/features/asset_hash.feature | 10 +- .../fixtures/asset-host-app/config.rb | 1 + .../lib/middleman-core/application.rb | 65 ++++---- middleman-core/lib/middleman-core/builder.rb | 46 +++--- .../lib/middleman-core/callback_manager.rb | 11 +- .../lib/middleman-core/core_extensions.rb | 6 + .../core_extensions/front_matter.rb | 1 + .../core_extensions/inline_url_rewriter.rb | 148 ++++++++++++++++++ .../middleman-core/extensions/asset_hash.rb | 38 ++--- .../middleman-core/extensions/asset_host.rb | 19 ++- .../middleman-core/extensions/cache_buster.rb | 18 +-- .../extensions/relative_assets.rb | 18 +-- .../lib/middleman-core/file_renderer.rb | 8 +- .../middleware/inline_url_rewriter.rb | 109 ------------- .../lib/middleman-core/sitemap/resource.rb | 6 +- .../lib/middleman-core/sitemap/store.rb | 43 +++-- middleman-core/lib/middleman-core/sources.rb | 6 +- .../middleman-core/sources/source_watcher.rb | 10 +- .../step_definitions/server_steps.rb | 16 +- middleman-core/lib/middleman-core/util.rb | 4 +- .../lib/middleman-core/util/data.rb | 8 +- 22 files changed, 360 insertions(+), 280 deletions(-) create mode 100644 middleman-core/lib/middleman-core/core_extensions/inline_url_rewriter.rb delete mode 100644 middleman-core/lib/middleman-core/middleware/inline_url_rewriter.rb diff --git a/middleman-cli/lib/middleman-cli/build.rb b/middleman-cli/lib/middleman-cli/build.rb index 1ef884b2..3ff1d638 100644 --- a/middleman-cli/lib/middleman-cli/build.rb +++ b/middleman-cli/lib/middleman-cli/build.rb @@ -48,31 +48,38 @@ module Middleman::Cli verbose = options['verbose'] ? 0 : 1 instrument = options['instrument'] - @app = ::Middleman::Application.new do - config[:mode] = :build - config[:environment] = env - config[:show_exceptions] = false - ::Middleman::Logger.singleton(verbose, instrument) + builder = nil + + ::Middleman::Logger.singleton(verbose, instrument) + + ::Middleman::Util.instrument "builder_setup" do + @app = ::Middleman::Application.new do + config[:mode] = :build + config[:environment] = env + config[:show_exceptions] = false + end + + builder = Middleman::Builder.new(@app, + glob: options['glob'], + clean: options['clean'], + parallel: options['parallel']) + builder.thor = self + builder.on_build_event(&method(:on_event)) end - builder = Middleman::Builder.new(@app, - glob: options['glob'], - clean: options['clean'], - parallel: options['parallel']) - builder.thor = self - builder.on_build_event(&method(:on_event)) + ::Middleman::Util.instrument "builder_run" do + if builder.run! + clean_directories! if options['clean'] + shell.say 'Project built successfully.' + else + msg = 'There were errors during this build' + unless options['verbose'] + msg << ', re-run with `middleman build --verbose` to see the full exception.' + end + shell.say msg, :red - if builder.run! - clean_directories! if options['clean'] - shell.say 'Project built successfully.' - else - msg = 'There were errors during this build' - unless options['verbose'] - msg << ', re-run with `middleman build --verbose` to see the full exception.' + exit(1) end - shell.say msg, :red - - exit(1) end end diff --git a/middleman-core/features/asset_hash.feature b/middleman-core/features/asset_hash.feature index 0a05c1a6..751b9e30 100644 --- a/middleman-core/features/asset_hash.feature +++ b/middleman-core/features/asset_hash.feature @@ -110,22 +110,22 @@ Feature: Assets get file hashes appended to them and references to them are upda Scenario: Enabling an asset host still produces hashed files and references Given the Server is running at "asset-hash-host-app" When I go to "/" - Then I should see 'href="http://middlemanapp.com/stylesheets/site-e587b659.css"' - Then I should see 'href="http://middlemanapp.com/stylesheets/fragment-7af0b5ab.css"' + Then I should see 'href="http://middlemanapp.com/stylesheets/site-4b64a653.css"' + Then I should see 'href="http://middlemanapp.com/stylesheets/fragment-a772891f.css"' And I should see 'src="http://middlemanapp.com/images/100px-5fd6fb90.jpg"' And I should see 'src="http://middlemanapp.com/images/100px-5fd6fb90.jpg?test"' And I should see 'src="http://middlemanapp.com/images/100px-5fd6fb90.jpg?#test"' And I should see 'src="http://middlemanapp.com/images/100px-5fd6fb90.jpg#test"' When I go to "/subdir/" - Then I should see 'href="http://middlemanapp.com/stylesheets/site-e587b659.css"' + Then I should see 'href="http://middlemanapp.com/stylesheets/site-4b64a653.css"' And I should see 'src="http://middlemanapp.com/images/100px-5fd6fb90.jpg"' When I go to "/other/" - Then I should see 'href="http://middlemanapp.com/stylesheets/site-e587b659.css"' + Then I should see 'href="http://middlemanapp.com/stylesheets/site-4b64a653.css"' And I should see 'src="http://middlemanapp.com/images/100px-5fd6fb90.jpg"' And I should see 'src="http://middlemanapp.com/images/100px-5fd6fb90.jpg?test"' And I should see 'src="http://middlemanapp.com/images/100px-5fd6fb90.jpg?#test"' And I should see 'src="http://middlemanapp.com/images/100px-5fd6fb90.jpg#test"' - When I go to "/stylesheets/fragment-7af0b5ab.css" + When I go to "/stylesheets/fragment-a772891f.css" And I should see 'url("http://middlemanapp.com/images/100px-5fd6fb90.jpg")' And I should see 'url("http://middlemanapp.com/images/100px-5fd6fb90.jpg?test")' And I should see 'url("http://middlemanapp.com/images/100px-5fd6fb90.jpg?#test")' diff --git a/middleman-core/fixtures/asset-host-app/config.rb b/middleman-core/fixtures/asset-host-app/config.rb index e69de29b..e470be25 100644 --- a/middleman-core/fixtures/asset-host-app/config.rb +++ b/middleman-core/fixtures/asset-host-app/config.rb @@ -0,0 +1 @@ + activate :asset_host, host: "http://assets1.example.com" diff --git a/middleman-core/lib/middleman-core/application.rb b/middleman-core/lib/middleman-core/application.rb index be603df4..659bd1ba 100644 --- a/middleman-core/lib/middleman-core/application.rb +++ b/middleman-core/lib/middleman-core/application.rb @@ -210,46 +210,51 @@ module Middleman # Search the root of the project for required files $LOAD_PATH.unshift(root) unless $LOAD_PATH.include?(root) - @callbacks = ::Middleman::CallbackManager.new - @callbacks.install_methods!(self, [ - :initialized, - :configure, - :before_extensions, - :before_sitemap, - :before_configuration, - :after_configuration, - :after_configuration_eval, - :ready, - :before_build, - :after_build, - :before_shutdown, - :before, # Before Rack requests - :before_render, - :after_render, - :before_server - ]) + ::Middleman::Util.instrument "application.setup" do + @callbacks = ::Middleman::CallbackManager.new + @callbacks.install_methods!(self, [ + :initialized, + :configure, + :before_extensions, + :before_instance_block, + :before_sitemap, + :before_configuration, + :after_configuration, + :after_configuration_eval, + :ready, + :before_build, + :after_build, + :before_shutdown, + :before, # Before Rack requests + :before_render, + :after_render, + :before_server + ]) - @middleware = Set.new - @mappings = Set.new + @middleware = Set.new + @mappings = Set.new - @template_context_class = Class.new(Middleman::TemplateContext) - @generic_template_context = @template_context_class.new(self) - @config_context = ConfigContext.new(self, @template_context_class) + @template_context_class = Class.new(Middleman::TemplateContext) + @generic_template_context = @template_context_class.new(self) + @config_context = ConfigContext.new(self, @template_context_class) - # Setup the default values from calls to set before initialization - @config = ::Middleman::Configuration::ConfigurationManager.new - @config.load_settings(self.class.config.all_settings) + # Setup the default values from calls to set before initialization + @config = ::Middleman::Configuration::ConfigurationManager.new + @config.load_settings(self.class.config.all_settings) - config[:source] = ENV['MM_SOURCE'] if ENV['MM_SOURCE'] + config[:source] = ENV['MM_SOURCE'] if ENV['MM_SOURCE'] - # TODO, make this less global - ::Middleman::FileRenderer.cache.clear - ::Middleman::TemplateRenderer.cache.clear + # TODO, make this less global + ::Middleman::FileRenderer.cache.clear + ::Middleman::TemplateRenderer.cache.clear + end execute_callbacks(:before_extensions) @extensions = ::Middleman::ExtensionManager.new(self) + execute_callbacks(:before_instance_block) + # Evaluate a passed block if given config_context.instance_exec(&block) if block_given? diff --git a/middleman-core/lib/middleman-core/builder.rb b/middleman-core/lib/middleman-core/builder.rb index 02feb15b..add35fe9 100644 --- a/middleman-core/lib/middleman-core/builder.rb +++ b/middleman-core/lib/middleman-core/builder.rb @@ -72,13 +72,17 @@ module Middleman Contract ResourceList def prerender_css logger.debug '== Prerendering CSS' - css_files = @app.sitemap.resources.select do |resource| - resource.ext == '.css' - end.each(&method(:output_resource)) - logger.debug '== Checking for Compass sprites' + + css_files = @app.sitemap.resources + .select { |resource| resource.ext == '.css' } + .each(&method(:output_resource)) + # Double-check for compass sprites - @app.files.find_new_files! - @app.sitemap.ensure_resource_list_updated! + if @app.files.find_new_files!.length > 0 + logger.debug '== Checking for Compass sprites' + @app.sitemap.ensure_resource_list_updated! + end + css_files end @@ -131,24 +135,26 @@ module Middleman # @return [void] Contract Pathname, Or[String, Pathname] => Any def export_file!(output_file, source) - source = write_tempfile(output_file, source.to_s) if source.is_a? String + # ::Middleman::Util.instrument "write_file", output_file: output_file do + source = write_tempfile(output_file, source.to_s) if source.is_a? String - method, source_path = if source.is_a? Tempfile - [FileUtils.method(:mv), source.path] - else - [FileUtils.method(:cp), source.to_s] - end + method, source_path = if source.is_a? Tempfile + [::FileUtils.method(:mv), source.path] + else + [::FileUtils.method(:cp), source.to_s] + end - mode = which_mode(output_file, source_path) + mode = which_mode(output_file, source_path) - if mode == :created || mode == :updated - FileUtils.mkdir_p(output_file.dirname) - method.call(source_path, output_file.to_s) - end + if mode == :created || mode == :updated + ::FileUtils.mkdir_p(output_file.dirname) + method.call(source_path, output_file.to_s) + end - source.unlink if source.is_a? Tempfile + source.unlink if source.is_a? Tempfile - trigger(mode, output_file) + trigger(mode, output_file) + # end end # Try to output a resource and capture errors. @@ -162,7 +168,7 @@ module Middleman if resource.binary? export_file!(output_file, resource.file_descriptor[:full_path]) else - response = @rack.get(URI.escape(resource.request_path)) + response = @rack.get(::URI.escape(resource.request_path)) # If we get a response, save it to a tempfile. if response.status == 200 diff --git a/middleman-core/lib/middleman-core/callback_manager.rb b/middleman-core/lib/middleman-core/callback_manager.rb index 4315256a..5db86302 100644 --- a/middleman-core/lib/middleman-core/callback_manager.rb +++ b/middleman-core/lib/middleman-core/callback_manager.rb @@ -48,8 +48,15 @@ module Middleman Contract Or[Symbol, ArrayOf[Symbol]], Maybe[ArrayOf[Any]], Maybe[RespondTo[:instance_exec]] => Any def execute(keys, args=[], scope=self) - callbacks_for(keys).each { |b| scope.instance_exec(*args, &b) } - @subscribers.each { |b| scope.instance_exec(keys, args, &b) } + callbacks = callbacks_for(keys) + callbacks_count = callbacks.length + @subscribers.length + + return if callbacks_count < 1 + + # ::Middleman::Util.instrument "callbacks.execute", keys: keys, length: callbacks_count do + callbacks.each { |b| scope.instance_exec(*args, &b) } + @subscribers.each { |b| scope.instance_exec(keys, args, &b) } + # end end Contract Or[Symbol, ArrayOf[Symbol]] => ::Hamster::Vector diff --git a/middleman-core/lib/middleman-core/core_extensions.rb b/middleman-core/lib/middleman-core/core_extensions.rb index ea31f89a..e38be937 100644 --- a/middleman-core/lib/middleman-core/core_extensions.rb +++ b/middleman-core/lib/middleman-core/core_extensions.rb @@ -19,6 +19,12 @@ Middleman::Extensions.register :data, auto_activate: :before_sitemap do Middleman::CoreExtensions::Data end +# Rewrite embedded URLs via Rack +Middleman::Extensions.register :inline_url_rewriter, auto_activate: :before_sitemap do + require 'middleman-core/core_extensions/inline_url_rewriter' + Middleman::CoreExtensions::InlineURLRewriter +end + # Catch and show exceptions at the Rack level Middleman::Extensions.register :show_exceptions, auto_activate: :before_configuration, modes: [:server] do require 'middleman-core/core_extensions/show_exceptions' 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 739b49fd..8bd31e4c 100644 --- a/middleman-core/lib/middleman-core/core_extensions/front_matter.rb +++ b/middleman-core/lib/middleman-core/core_extensions/front_matter.rb @@ -30,6 +30,7 @@ module Middleman::CoreExtensions resources.each do |resource| next if resource.ignored? next if resource.file_descriptor.nil? + next unless resource.template? fmdata = data(resource.file_descriptor[:full_path].to_s).first.dup 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 new file mode 100644 index 00000000..2d04902d --- /dev/null +++ b/middleman-core/lib/middleman-core/core_extensions/inline_url_rewriter.rb @@ -0,0 +1,148 @@ +require 'rack' +require 'rack/response' +require 'addressable/uri' +require 'middleman-core/util' +require 'middleman-core/contracts' + +module Middleman + module CoreExtensions + + class InlineURLRewriter < ::Middleman::Extension + include Contracts + + expose_to_application rewrite_inline_urls: :add + + IGNORE_DESCRIPTOR = Or[Regexp, RespondTo[:call], String] + REWRITER_DESCRIPTOR = { + id: Symbol, + proc: Or[Proc, Method], + url_extensions: ArrayOf[String], + source_extensions: ArrayOf[String], + ignore: ArrayOf[IGNORE_DESCRIPTOR] + } + + def initialize(app, options_hash={}, &block) + super + + @rewriters = {} + end + + Contract REWRITER_DESCRIPTOR => Any + def add(options) + @rewriters[options] = options + end + + def after_configuration + app.use Rack, { + rewriters: @rewriters.values, + middleman_app: @app + } + end + + class Rack + include Contracts + + Contract RespondTo[:call], ({ + middleman_app: IsA['Middleman::Application'], + rewriters: ArrayOf[REWRITER_DESCRIPTOR] + }) => Any + def initialize(app, options={}) + @rack_app = app + @middleman_app = options.fetch(:middleman_app) + @rewriters = options.fetch(:rewriters) + end + + def call(env) + status, headers, response = @rack_app.call(env) + + # 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 body = ::Middleman::Util.extract_response_text(response) + + dirpath = ::Pathname.new(File.dirname(path)) + + rewritten = nil + + # ::Middleman::Util.instrument "inline_url_rewriter", path: path do + rewritten = ::Middleman::Util.rewrite_paths(body, path, all_asset_exts) do |asset_path| + uri = ::Addressable::URI.parse(asset_path) + + relative_path = uri.host.nil? + + full_asset_path = if relative_path + dirpath.join(asset_path).to_s + else + asset_path + end + + @rewriters.each do |rewriter| + uid = rewriter.fetch(:id) + + # Allow upstream request to skip this specific rewriting + next if env["bypass_inline_url_rewriter_#{uid}"] == 'true' + + exts = rewriter.fetch(:url_extensions) + next unless exts.include?(::File.extname(asset_path)) + + source_exts = rewriter.fetch(:source_extensions) + next unless source_exts.include?(::File.extname(path)) + + ignore = rewriter.fetch(:ignore) + next if ignore.any? { |r| should_ignore?(r, full_asset_path) } + + rewrite_ignore = Array(rewriter.fetch(:rewrite_ignore, [])) + next if rewrite_ignore.any? { |ignore| ::Middleman::Util.path_match(ignore, path) } + + proc = rewriter.fetch(:proc) + + result = proc.call(asset_path, dirpath, path) + asset_path = result if result + end + + asset_path + # end + end + + ::Rack::Response.new( + rewritten, + status, + headers + ).finish + end + + Contract IGNORE_DESCRIPTOR, String => Bool + def should_ignore?(validator, value) + if validator.is_a? Regexp + # Treat as Regexp + !value.match(validator).nil? + elsif validator.respond_to? :call + # Treat as proc + validator.call(value) + elsif validator.is_a? String + # Treat as glob + File.fnmatch(value, validator) + else + # If some unknown thing, don't ignore + false + end + end + end + 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 da84a23d..e33ebf34 100644 --- a/middleman-core/lib/middleman-core/extensions/asset_hash.rb +++ b/middleman-core/lib/middleman-core/extensions/asset_hash.rb @@ -1,8 +1,8 @@ -require 'addressable/uri' require 'middleman-core/util' require 'middleman-core/rack' class Middleman::Extensions::AssetHash < ::Middleman::Extension + option :sources, %w(.htm .html .php .css .js), 'List of extensions that are searched for hashable assets.' option :exts, %w(.jpg .jpeg .png .gif .webp .js .css .otf .woff .woff2 .eot .ttf .svg .svgz), 'List of extensions that get asset hashes appended to them.' option :ignore, [], 'Regexes of filenames to skip adding asset hashes to' option :rewrite_ignore, [], 'Regexes of filenames to skip processing for path rewrites' @@ -10,23 +10,19 @@ class Middleman::Extensions::AssetHash < ::Middleman::Extension def initialize(app, options_hash={}, &block) super + require 'addressable/uri' require 'digest/sha1' require 'rack/mock' - require 'middleman-core/middleware/inline_url_rewriter' - end - def after_configuration # Allow specifying regexes to ignore, plus always ignore apple touch icons @ignore = Array(options.ignore) + [/^apple-touch-icon/] - app.use ::Middleman::Middleware::InlineURLRewriter, - id: :asset_hash, - url_extensions: options.exts.sort.reverse, - source_extensions: %w(.htm .html .php .css .js), - ignore: @ignore, - rewrite_ignore: options.rewrite_ignore, - middleman_app: app, - proc: method(:rewrite_url) + app.rewrite_inline_urls id: :asset_hash, + url_extensions: options.exts.sort.reverse, + source_extensions: options.sources, + ignore: @ignore, + rewrite_ignore: options.rewrite_ignore, + proc: method(:rewrite_url) end Contract String, Or[String, Pathname], Any => Maybe[String] @@ -77,15 +73,19 @@ class Middleman::Extensions::AssetHash < ::Middleman::Extension return if ignored_resource?(resource) return if resource.ignored? - # Render through the Rack interface so middleware and mounted apps get a shot - response = @rack_client.get( - URI.escape(resource.destination_path), - 'bypass_inline_url_rewriter_asset_hash' => 'true' - ) + digest = if resource.template? + # Render through the Rack interface so middleware and mounted apps get a shot + response = @rack_client.get( + ::URI.escape(resource.destination_path), + 'bypass_inline_url_rewriter_asset_hash' => 'true' + ) - raise "#{resource.path} should be in the sitemap!" unless response.status == 200 + raise "#{resource.path} should be in the sitemap!" unless response.status == 200 - digest = Digest::SHA1.hexdigest(response.body)[0..7] + ::Digest::SHA1.hexdigest(response.body)[0..7] + else + ::Digest::SHA1.file(resource.source_file).hexdigest[0..7] + end resource.destination_path = resource.destination_path.sub(/\.(\w+)$/) { |ext| "-#{digest}#{ext}" } resource diff --git a/middleman-core/lib/middleman-core/extensions/asset_host.rb b/middleman-core/lib/middleman-core/extensions/asset_host.rb index 05b4509f..00d463fa 100644 --- a/middleman-core/lib/middleman-core/extensions/asset_host.rb +++ b/middleman-core/lib/middleman-core/extensions/asset_host.rb @@ -1,5 +1,4 @@ require 'addressable/uri' -require 'middleman-core/middleware/inline_url_rewriter' class Middleman::Extensions::AssetHost < ::Middleman::Extension option :host, nil, 'The asset host to use or a Proc to determine asset host', required: true @@ -8,15 +7,15 @@ class Middleman::Extensions::AssetHost < ::Middleman::Extension option :ignore, [], 'Regexes of filenames to skip adding query strings to' option :rewrite_ignore, [], 'Regexes of filenames to skip processing for host rewrites' - def ready - app.use ::Middleman::Middleware::InlineURLRewriter, - id: :asset_host, - url_extensions: options.exts, - source_extensions: options.sources, - ignore: options.ignore, - rewrite_ignore: options.rewrite_ignore, - middleman_app: app, - proc: method(:rewrite_url) + def initialize(app, options_hash={}, &block) + super + + app.rewrite_inline_urls id: :asset_host, + url_extensions: options.exts, + source_extensions: options.sources, + ignore: options.ignore, + rewrite_ignore: options.rewrite_ignore, + proc: method(:rewrite_url) end Contract String, Or[String, Pathname], Any => String diff --git a/middleman-core/lib/middleman-core/extensions/cache_buster.rb b/middleman-core/lib/middleman-core/extensions/cache_buster.rb index 8f1ceb64..44eda9bc 100644 --- a/middleman-core/lib/middleman-core/extensions/cache_buster.rb +++ b/middleman-core/lib/middleman-core/extensions/cache_buster.rb @@ -8,18 +8,12 @@ class Middleman::Extensions::CacheBuster < ::Middleman::Extension def initialize(app, options_hash={}, &block) super - require 'middleman-core/middleware/inline_url_rewriter' - end - - def after_configuration - app.use ::Middleman::Middleware::InlineURLRewriter, - id: :cache_buster, - url_extensions: options.exts, - source_extensions: options.sources, - ignore: options.ignore, - rewrite_ignore: options.rewrite_ignore, - middleman_app: app, - proc: method(:rewrite_url) + app.rewrite_inline_urls id: :cache_buster, + url_extensions: options.exts, + source_extensions: options.sources, + ignore: options.ignore, + rewrite_ignore: options.rewrite_ignore, + proc: method(:rewrite_url) end Contract String, Or[String, Pathname], Any => String diff --git a/middleman-core/lib/middleman-core/extensions/relative_assets.rb b/middleman-core/lib/middleman-core/extensions/relative_assets.rb index f232f6a4..a6d1f074 100644 --- a/middleman-core/lib/middleman-core/extensions/relative_assets.rb +++ b/middleman-core/lib/middleman-core/extensions/relative_assets.rb @@ -10,18 +10,12 @@ class Middleman::Extensions::RelativeAssets < ::Middleman::Extension def initialize(app, options_hash={}, &block) super - require 'middleman-core/middleware/inline_url_rewriter' - end - - def ready - app.use ::Middleman::Middleware::InlineURLRewriter, - id: :asset_hash, - url_extensions: options.exts, - source_extensions: options.sources, - ignore: options.ignore, - rewrite_ignore: options.rewrite_ignore, - middleman_app: app, - proc: method(:rewrite_url) + app.rewrite_inline_urls id: :asset_hash, + url_extensions: options.exts, + source_extensions: options.sources, + ignore: options.ignore, + rewrite_ignore: options.rewrite_ignore, + proc: method(:rewrite_url) end helpers do diff --git a/middleman-core/lib/middleman-core/file_renderer.rb b/middleman-core/lib/middleman-core/file_renderer.rb index 09c6b5d2..1eb551d9 100644 --- a/middleman-core/lib/middleman-core/file_renderer.rb +++ b/middleman-core/lib/middleman-core/file_renderer.rb @@ -73,9 +73,11 @@ module Middleman # end # Render using Tilt - content = ::Middleman::Util.instrument 'render.tilt', path: path do - template.render(context, locs, &block) - end + content = nil + + # ::Middleman::Util.instrument 'render.tilt', path: path do + content = template.render(context, locs, &block) + # end # Allow hooks to manipulate the result after render content = @app.callbacks_for(:after_render).reduce(content) do |sum, callback| diff --git a/middleman-core/lib/middleman-core/middleware/inline_url_rewriter.rb b/middleman-core/lib/middleman-core/middleware/inline_url_rewriter.rb deleted file mode 100644 index b76c3285..00000000 --- a/middleman-core/lib/middleman-core/middleware/inline_url_rewriter.rb +++ /dev/null @@ -1,109 +0,0 @@ -require 'rack' -require 'rack/response' -require 'addressable/uri' -require 'middleman-core/util' -require 'middleman-core/contracts' - -module Middleman - module Middleware - class InlineURLRewriter - include Contracts - - IGNORE_DESCRIPTOR = Or[Regexp, RespondTo[:call], String] - - Contract RespondTo[:call], ({ - middleman_app: IsA['Middleman::Application'], - id: Maybe[Symbol], - proc: Or[Proc, Method], - url_extensions: ArrayOf[String], - source_extensions: ArrayOf[String], - ignore: ArrayOf[IGNORE_DESCRIPTOR] - }) => Any - def initialize(app, options={}) - @rack_app = app - @middleman_app = options.fetch(:middleman_app) - - @uid = options.fetch(:id, nil) - @proc = options.fetch(:proc) - - raise 'InlineURLRewriter requires a :proc to call with inline URL results' unless @proc - - @exts = options.fetch(:url_extensions) - - @source_exts = options.fetch(:source_extensions) - @source_exts_regex_text = Regexp.union(@source_exts).to_s - - @ignore = options.fetch(:ignore) - @rewrite_ignore = Array(options.fetch(:rewrite_ignore, [])) - end - - def call(env) - status, headers, response = @rack_app.call(env) - - # Allow configuration or upstream request to skip all rewriting - if rewrite_ignore?(env['PATH_INFO']) || env['bypass_inline_url_rewriter'] == 'true' - return [status, headers, response] - end - - # Allow upstream request to skip this specific rewriting - if @uid - uid_key = "bypass_inline_url_rewriter_#{@uid}" - return [status, headers, response] if env[uid_key] == 'true' - end - - path = ::Middleman::Util.full_path(env['PATH_INFO'], @middleman_app) - - if path =~ /(^\/$)|(#{@source_exts_regex_text}$)/ - if body = ::Middleman::Util.extract_response_text(response) - - dirpath = Pathname.new(File.dirname(path)) - - rewritten = ::Middleman::Util.rewrite_paths(body, path, @exts) do |asset_path| - uri = ::Addressable::URI.parse(asset_path) - - relative_path = uri.host.nil? - - full_asset_path = if relative_path - dirpath.join(asset_path).to_s - else - asset_path - end - - @ignore.none? { |r| should_ignore?(r, full_asset_path) } && @proc.call(asset_path, dirpath, path) - end - - status, headers, response = ::Rack::Response.new( - rewritten, - status, - headers - ).finish - end - end - - [status, headers, response] - end - - Contract IGNORE_DESCRIPTOR, String => Bool - def should_ignore?(validator, value) - if validator.is_a? Regexp - # Treat as Regexp - !value.match(validator).nil? - elsif validator.respond_to? :call - # Treat as proc - validator.call(value) - elsif validator.is_a? String - # Treat as glob - File.fnmatch(value, validator) - else - # If some unknown thing, don't ignore - false - end - end - - Contract String => Bool - def rewrite_ignore?(path) - @rewrite_ignore.any? { |ignore| Middleman::Util.path_match(ignore, path) } - end - end - end -end diff --git a/middleman-core/lib/middleman-core/sitemap/resource.rb b/middleman-core/lib/middleman-core/sitemap/resource.rb index 7a13e9f9..866fff41 100644 --- a/middleman-core/lib/middleman-core/sitemap/resource.rb +++ b/middleman-core/lib/middleman-core/sitemap/resource.rb @@ -127,9 +127,9 @@ module Middleman # @return [String] Contract Hash, Hash => String def render(opts={}, locs={}) - return ::Middleman::FileRenderer.new(@app, file_descriptor[:full_path].to_s).template_data_for_file unless template? + return File.read(file_descriptor[:full_path].to_s) unless template? - ::Middleman::Util.instrument 'render.resource', path: file_descriptor[:full_path].to_s, destination_path: destination_path do + # ::Middleman::Util.instrument 'render.resource', path: file_descriptor[:full_path].to_s, destination_path: destination_path do md = metadata opts = md[:options].deep_merge(opts) locs = md[:locals].deep_merge(locs) @@ -140,7 +140,7 @@ module Middleman renderer = ::Middleman::TemplateRenderer.new(@app, file_descriptor[:full_path].to_s) renderer.render(locs, opts) - end + # end end # A path without the directory index - so foo/index.html becomes diff --git a/middleman-core/lib/middleman-core/sitemap/store.rb b/middleman-core/lib/middleman-core/sitemap/store.rb index 985b3ae4..4d938cac 100644 --- a/middleman-core/lib/middleman-core/sitemap/store.rb +++ b/middleman-core/lib/middleman-core/sitemap/store.rb @@ -75,6 +75,7 @@ module Middleman def initialize(app) @app = app @resources = [] + @rebuild_reasons = [:first_run] @update_count = 0 @resource_list_manipulators = ::Hamster::Vector.empty @@ -115,9 +116,10 @@ module Middleman # Rebuild the list of resources from scratch, using registed manipulators # @return [void] - Contract Maybe[Symbol] => Any - def rebuild_resource_list!(name=nil) + Contract Symbol => Any + def rebuild_resource_list!(name) @lock.synchronize do + @rebuild_reasons << name @app.logger.debug "== Requesting resource list rebuilding: #{name}" @needs_sitemap_rebuild = true end @@ -198,29 +200,36 @@ module Middleman def ensure_resource_list_updated! @lock.synchronize do return unless @needs_sitemap_rebuild - @needs_sitemap_rebuild = false - @app.logger.debug '== Rebuilding resource list' + ::Middleman::Util.instrument "sitemap.update", reasons: @rebuild_reasons.uniq do + @needs_sitemap_rebuild = false - @resources = [] + @app.logger.debug '== Rebuilding resource list' - @resource_list_manipulators.each do |m| - @app.logger.debug "== Running manipulator: #{m[:name]}" - @resources = m[:manipulator].send(m[:custom_name] || :manipulate_resource_list, @resources) + @resources = [] - # Reset lookup cache - reset_lookup_cache! + @resource_list_manipulators.each do |m| + ::Middleman::Util.instrument "sitemap.manipulator", name: m[:name] do + @app.logger.debug "== Running manipulator: #{m[:name]}" + @resources = m[:manipulator].send(m[:custom_name] || :manipulate_resource_list, @resources) - # Rebuild cache - @resources.each do |resource| - @_lookup_by_path[resource.path] = resource - @_lookup_by_destination_path[resource.destination_path] = resource + # Reset lookup cache + reset_lookup_cache! + + # Rebuild cache + @resources.each do |resource| + @_lookup_by_path[resource.path] = resource + @_lookup_by_destination_path[resource.destination_path] = resource + end + + invalidate_resources_not_ignored_cache! + end end - invalidate_resources_not_ignored_cache! - end + @update_count += 1 - @update_count += 1 + @rebuild_reasons = [] + end end end diff --git a/middleman-core/lib/middleman-core/sources.rb b/middleman-core/lib/middleman-core/sources.rb index 6b888a87..44c3e316 100644 --- a/middleman-core/lib/middleman-core/sources.rb +++ b/middleman-core/lib/middleman-core/sources.rb @@ -213,12 +213,12 @@ module Middleman # Manually poll all watchers for new content. # # @return [void] - Contract Any + Contract ArrayOf[Pathname] def find_new_files! - return unless @update_count != @last_update_count + return [] unless @update_count != @last_update_count @last_update_count = @update_count - watchers.each(&:poll_once!) + watchers.reduce([]) { |sum, w| sum + w.poll_once! } end # Start up all listeners. diff --git a/middleman-core/lib/middleman-core/sources/source_watcher.rb b/middleman-core/lib/middleman-core/sources/source_watcher.rb index 00e500fb..6d667db0 100644 --- a/middleman-core/lib/middleman-core/sources/source_watcher.rb +++ b/middleman-core/lib/middleman-core/sources/source_watcher.rb @@ -180,17 +180,19 @@ module Middleman # Manually trigger update events. # # @return [void] - Contract Any + Contract ArrayOf[Pathname] def poll_once! updated = ::Middleman::Util.all_files_under(@directory.to_s) removed = @files.keys.reject { |p| updated.include?(p) } update(updated, removed) - return unless @waiting_for_existence && @directory.exist? + if @waiting_for_existence && @directory.exist? + @waiting_for_existence = false + listen! + end - @waiting_for_existence = false - listen! + updated + removed end # Work around this bug: http://bugs.ruby-lang.org/issues/4521 diff --git a/middleman-core/lib/middleman-core/step_definitions/server_steps.rb b/middleman-core/lib/middleman-core/step_definitions/server_steps.rb index 7defbbc0..5f32e691 100644 --- a/middleman-core/lib/middleman-core/step_definitions/server_steps.rb +++ b/middleman-core/lib/middleman-core/step_definitions/server_steps.rb @@ -4,22 +4,23 @@ require 'capybara/cucumber' Given /^a clean server$/ do @initialize_commands = [] + @activation_commands = [] end Given /^"([^\"]*)" feature is "([^\"]*)"$/ do |feature, state| - @initialize_commands ||= [] + @activation_commands ||= [] if state == 'enabled' - @initialize_commands << lambda { activate(feature.to_sym) } + @activation_commands << lambda { activate(feature.to_sym) } end end Given /^"([^\"]*)" feature is "enabled" with "([^\"]*)"$/ do |feature, options_str| - @initialize_commands ||= [] + @activation_commands ||= [] options = eval("{#{options_str}}") - @initialize_commands << lambda { activate(feature.to_sym, options) } + @activation_commands << lambda { activate(feature.to_sym, options) } end Given /^"([^\"]*)" is set to "([^\"]*)"$/ do |variable, value| @@ -41,6 +42,7 @@ Given /^the Server is running$/ do ENV['MM_ROOT'] = root_dir initialize_commands = @initialize_commands || [] + activation_commands = @activation_commands || [] @server_inst = ::Middleman::Application.new do config[:watcher_disable] = true @@ -49,6 +51,12 @@ Given /^the Server is running$/ do initialize_commands.each do |p| instance_exec(&p) end + + app.after_configuration_eval do + activation_commands.each do |p| + config_context.instance_exec(&p) + end + end end Capybara.app = ::Middleman::Rack.new(@server_inst).to_app diff --git a/middleman-core/lib/middleman-core/util.rb b/middleman-core/lib/middleman-core/util.rb index c4210a0e..ec6b1854 100644 --- a/middleman-core/lib/middleman-core/util.rb +++ b/middleman-core/lib/middleman-core/util.rb @@ -332,7 +332,7 @@ module Middleman end Contract String, String, ArrayOf[String], Proc => String - def rewrite_paths(body, _path, exts, &_block) + def rewrite_paths(body, _path, exts, &block) matcher = /([=\'\"\(,]\s*)([^\s\'\"\)>]+(#{Regexp.union(exts)}))/ url_fn_prefix = 'url(' @@ -349,7 +349,7 @@ module Middleman begin uri = ::Addressable::URI.parse(asset_path) - if uri.relative? && uri.host.nil? && (result = yield(asset_path)) + if uri.relative? && uri.host.nil? && (result = block.call(asset_path)) "#{opening_character}#{result}" else match diff --git a/middleman-core/lib/middleman-core/util/data.rb b/middleman-core/lib/middleman-core/util/data.rb index c1dc1cd3..6670f989 100644 --- a/middleman-core/lib/middleman-core/util/data.rb +++ b/middleman-core/lib/middleman-core/util/data.rb @@ -17,7 +17,7 @@ module Middleman # @return [Array] Contract Pathname, Maybe[Symbol] => [Hash, Maybe[String]] def parse(full_path, frontmatter_delims, known_type=nil) - return [{}, nil] if Middleman::Util.binary?(full_path) + return [{}, nil] if ::Middleman::Util.binary?(full_path) # Avoid weird race condition when a file is renamed begin @@ -74,8 +74,8 @@ module Middleman # @return [Hash] Contract String, Pathname, Bool => Hash def parse_yaml(content, full_path) - symbolize_recursive(YAML.load(content) || {}) - rescue StandardError, Psych::SyntaxError => error + symbolize_recursive(::YAML.load(content) || {}) + rescue StandardError, ::Psych::SyntaxError => error warn "YAML Exception parsing #{full_path}: #{error.message}" {} end @@ -85,7 +85,7 @@ module Middleman # @return [Hash] Contract String, Pathname => Hash def parse_json(content, full_path) - symbolize_recursive(JSON.parse(content) || {}) + symbolize_recursive(::JSON.parse(content) || {}) rescue StandardError => error warn "JSON Exception parsing #{full_path}: #{error.message}" {}