perf
Thomas Reynolds 2016-01-13 17:16:36 -08:00
parent c213bd19df
commit bf19bbfed7
22 changed files with 360 additions and 280 deletions

View File

@ -48,31 +48,38 @@ module Middleman::Cli
verbose = options['verbose'] ? 0 : 1 verbose = options['verbose'] ? 0 : 1
instrument = options['instrument'] instrument = options['instrument']
@app = ::Middleman::Application.new do builder = nil
config[:mode] = :build
config[:environment] = env ::Middleman::Logger.singleton(verbose, instrument)
config[:show_exceptions] = false
::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 end
builder = Middleman::Builder.new(@app, ::Middleman::Util.instrument "builder_run" do
glob: options['glob'], if builder.run!
clean: options['clean'], clean_directories! if options['clean']
parallel: options['parallel']) shell.say 'Project built successfully.'
builder.thor = self else
builder.on_build_event(&method(:on_event)) 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! exit(1)
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 end
shell.say msg, :red
exit(1)
end end
end end

View File

@ -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 Scenario: Enabling an asset host still produces hashed files and references
Given the Server is running at "asset-hash-host-app" Given the Server is running at "asset-hash-host-app"
When I go to "/" 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/site-4b64a653.css"'
Then I should see 'href="http://middlemanapp.com/stylesheets/fragment-7af0b5ab.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"'
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"' 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/" 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"' And I should see 'src="http://middlemanapp.com/images/100px-5fd6fb90.jpg"'
When I go to "/other/" 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"'
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"' 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")'
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")'
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")'

View File

@ -0,0 +1 @@
activate :asset_host, host: "http://assets1.example.com"

View File

@ -210,46 +210,51 @@ module Middleman
# Search the root of the project for required files # Search the root of the project for required files
$LOAD_PATH.unshift(root) unless $LOAD_PATH.include?(root) $LOAD_PATH.unshift(root) unless $LOAD_PATH.include?(root)
@callbacks = ::Middleman::CallbackManager.new ::Middleman::Util.instrument "application.setup" do
@callbacks.install_methods!(self, [ @callbacks = ::Middleman::CallbackManager.new
:initialized, @callbacks.install_methods!(self, [
:configure, :initialized,
:before_extensions, :configure,
:before_sitemap, :before_extensions,
:before_configuration, :before_instance_block,
:after_configuration, :before_sitemap,
:after_configuration_eval, :before_configuration,
:ready, :after_configuration,
:before_build, :after_configuration_eval,
:after_build, :ready,
:before_shutdown, :before_build,
:before, # Before Rack requests :after_build,
:before_render, :before_shutdown,
:after_render, :before, # Before Rack requests
:before_server :before_render,
]) :after_render,
:before_server
])
@middleware = Set.new @middleware = Set.new
@mappings = Set.new @mappings = Set.new
@template_context_class = Class.new(Middleman::TemplateContext) @template_context_class = Class.new(Middleman::TemplateContext)
@generic_template_context = @template_context_class.new(self) @generic_template_context = @template_context_class.new(self)
@config_context = ConfigContext.new(self, @template_context_class) @config_context = ConfigContext.new(self, @template_context_class)
# Setup the default values from calls to set before initialization # Setup the default values from calls to set before initialization
@config = ::Middleman::Configuration::ConfigurationManager.new @config = ::Middleman::Configuration::ConfigurationManager.new
@config.load_settings(self.class.config.all_settings) @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 # TODO, make this less global
::Middleman::FileRenderer.cache.clear ::Middleman::FileRenderer.cache.clear
::Middleman::TemplateRenderer.cache.clear ::Middleman::TemplateRenderer.cache.clear
end
execute_callbacks(:before_extensions) execute_callbacks(:before_extensions)
@extensions = ::Middleman::ExtensionManager.new(self) @extensions = ::Middleman::ExtensionManager.new(self)
execute_callbacks(:before_instance_block)
# Evaluate a passed block if given # Evaluate a passed block if given
config_context.instance_exec(&block) if block_given? config_context.instance_exec(&block) if block_given?

View File

@ -72,13 +72,17 @@ module Middleman
Contract ResourceList Contract ResourceList
def prerender_css def prerender_css
logger.debug '== Prerendering CSS' logger.debug '== Prerendering CSS'
css_files = @app.sitemap.resources.select do |resource|
resource.ext == '.css' css_files = @app.sitemap.resources
end.each(&method(:output_resource)) .select { |resource| resource.ext == '.css' }
logger.debug '== Checking for Compass sprites' .each(&method(:output_resource))
# Double-check for compass sprites # Double-check for compass sprites
@app.files.find_new_files! if @app.files.find_new_files!.length > 0
@app.sitemap.ensure_resource_list_updated! logger.debug '== Checking for Compass sprites'
@app.sitemap.ensure_resource_list_updated!
end
css_files css_files
end end
@ -131,24 +135,26 @@ module Middleman
# @return [void] # @return [void]
Contract Pathname, Or[String, Pathname] => Any Contract Pathname, Or[String, Pathname] => Any
def export_file!(output_file, source) 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 method, source_path = if source.is_a? Tempfile
[FileUtils.method(:mv), source.path] [::FileUtils.method(:mv), source.path]
else else
[FileUtils.method(:cp), source.to_s] [::FileUtils.method(:cp), source.to_s]
end end
mode = which_mode(output_file, source_path) mode = which_mode(output_file, source_path)
if mode == :created || mode == :updated if mode == :created || mode == :updated
FileUtils.mkdir_p(output_file.dirname) ::FileUtils.mkdir_p(output_file.dirname)
method.call(source_path, output_file.to_s) method.call(source_path, output_file.to_s)
end 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 end
# Try to output a resource and capture errors. # Try to output a resource and capture errors.
@ -162,7 +168,7 @@ module Middleman
if resource.binary? if resource.binary?
export_file!(output_file, resource.file_descriptor[:full_path]) export_file!(output_file, resource.file_descriptor[:full_path])
else 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 we get a response, save it to a tempfile.
if response.status == 200 if response.status == 200

View File

@ -48,8 +48,15 @@ module Middleman
Contract Or[Symbol, ArrayOf[Symbol]], Maybe[ArrayOf[Any]], Maybe[RespondTo[:instance_exec]] => Any Contract Or[Symbol, ArrayOf[Symbol]], Maybe[ArrayOf[Any]], Maybe[RespondTo[:instance_exec]] => Any
def execute(keys, args=[], scope=self) def execute(keys, args=[], scope=self)
callbacks_for(keys).each { |b| scope.instance_exec(*args, &b) } callbacks = callbacks_for(keys)
@subscribers.each { |b| scope.instance_exec(keys, args, &b) } 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 end
Contract Or[Symbol, ArrayOf[Symbol]] => ::Hamster::Vector Contract Or[Symbol, ArrayOf[Symbol]] => ::Hamster::Vector

View File

@ -19,6 +19,12 @@ Middleman::Extensions.register :data, auto_activate: :before_sitemap do
Middleman::CoreExtensions::Data Middleman::CoreExtensions::Data
end 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 # Catch and show exceptions at the Rack level
Middleman::Extensions.register :show_exceptions, auto_activate: :before_configuration, modes: [:server] do Middleman::Extensions.register :show_exceptions, auto_activate: :before_configuration, modes: [:server] do
require 'middleman-core/core_extensions/show_exceptions' require 'middleman-core/core_extensions/show_exceptions'

View File

@ -30,6 +30,7 @@ module Middleman::CoreExtensions
resources.each do |resource| resources.each do |resource|
next if resource.ignored? next if resource.ignored?
next if resource.file_descriptor.nil? next if resource.file_descriptor.nil?
next unless resource.template?
fmdata = data(resource.file_descriptor[:full_path].to_s).first.dup fmdata = data(resource.file_descriptor[:full_path].to_s).first.dup

View File

@ -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

View File

@ -1,8 +1,8 @@
require 'addressable/uri'
require 'middleman-core/util' require 'middleman-core/util'
require 'middleman-core/rack' require 'middleman-core/rack'
class Middleman::Extensions::AssetHash < ::Middleman::Extension 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 :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 :ignore, [], 'Regexes of filenames to skip adding asset hashes to'
option :rewrite_ignore, [], 'Regexes of filenames to skip processing for path rewrites' 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) def initialize(app, options_hash={}, &block)
super super
require 'addressable/uri'
require 'digest/sha1' require 'digest/sha1'
require 'rack/mock' 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 # Allow specifying regexes to ignore, plus always ignore apple touch icons
@ignore = Array(options.ignore) + [/^apple-touch-icon/] @ignore = Array(options.ignore) + [/^apple-touch-icon/]
app.use ::Middleman::Middleware::InlineURLRewriter, app.rewrite_inline_urls id: :asset_hash,
id: :asset_hash, url_extensions: options.exts.sort.reverse,
url_extensions: options.exts.sort.reverse, source_extensions: options.sources,
source_extensions: %w(.htm .html .php .css .js), ignore: @ignore,
ignore: @ignore, rewrite_ignore: options.rewrite_ignore,
rewrite_ignore: options.rewrite_ignore, proc: method(:rewrite_url)
middleman_app: app,
proc: method(:rewrite_url)
end end
Contract String, Or[String, Pathname], Any => Maybe[String] 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 ignored_resource?(resource)
return if resource.ignored? return if resource.ignored?
# Render through the Rack interface so middleware and mounted apps get a shot digest = if resource.template?
response = @rack_client.get( # Render through the Rack interface so middleware and mounted apps get a shot
URI.escape(resource.destination_path), response = @rack_client.get(
'bypass_inline_url_rewriter_asset_hash' => 'true' ::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.destination_path = resource.destination_path.sub(/\.(\w+)$/) { |ext| "-#{digest}#{ext}" }
resource resource

View File

@ -1,5 +1,4 @@
require 'addressable/uri' require 'addressable/uri'
require 'middleman-core/middleware/inline_url_rewriter'
class Middleman::Extensions::AssetHost < ::Middleman::Extension class Middleman::Extensions::AssetHost < ::Middleman::Extension
option :host, nil, 'The asset host to use or a Proc to determine asset host', required: true 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 :ignore, [], 'Regexes of filenames to skip adding query strings to'
option :rewrite_ignore, [], 'Regexes of filenames to skip processing for host rewrites' option :rewrite_ignore, [], 'Regexes of filenames to skip processing for host rewrites'
def ready def initialize(app, options_hash={}, &block)
app.use ::Middleman::Middleware::InlineURLRewriter, super
id: :asset_host,
url_extensions: options.exts, app.rewrite_inline_urls id: :asset_host,
source_extensions: options.sources, url_extensions: options.exts,
ignore: options.ignore, source_extensions: options.sources,
rewrite_ignore: options.rewrite_ignore, ignore: options.ignore,
middleman_app: app, rewrite_ignore: options.rewrite_ignore,
proc: method(:rewrite_url) proc: method(:rewrite_url)
end end
Contract String, Or[String, Pathname], Any => String Contract String, Or[String, Pathname], Any => String

View File

@ -8,18 +8,12 @@ class Middleman::Extensions::CacheBuster < ::Middleman::Extension
def initialize(app, options_hash={}, &block) def initialize(app, options_hash={}, &block)
super super
require 'middleman-core/middleware/inline_url_rewriter' app.rewrite_inline_urls id: :cache_buster,
end url_extensions: options.exts,
source_extensions: options.sources,
def after_configuration ignore: options.ignore,
app.use ::Middleman::Middleware::InlineURLRewriter, rewrite_ignore: options.rewrite_ignore,
id: :cache_buster, proc: method(:rewrite_url)
url_extensions: options.exts,
source_extensions: options.sources,
ignore: options.ignore,
rewrite_ignore: options.rewrite_ignore,
middleman_app: app,
proc: method(:rewrite_url)
end end
Contract String, Or[String, Pathname], Any => String Contract String, Or[String, Pathname], Any => String

View File

@ -10,18 +10,12 @@ class Middleman::Extensions::RelativeAssets < ::Middleman::Extension
def initialize(app, options_hash={}, &block) def initialize(app, options_hash={}, &block)
super super
require 'middleman-core/middleware/inline_url_rewriter' app.rewrite_inline_urls id: :asset_hash,
end url_extensions: options.exts,
source_extensions: options.sources,
def ready ignore: options.ignore,
app.use ::Middleman::Middleware::InlineURLRewriter, rewrite_ignore: options.rewrite_ignore,
id: :asset_hash, proc: method(:rewrite_url)
url_extensions: options.exts,
source_extensions: options.sources,
ignore: options.ignore,
rewrite_ignore: options.rewrite_ignore,
middleman_app: app,
proc: method(:rewrite_url)
end end
helpers do helpers do

View File

@ -73,9 +73,11 @@ module Middleman
# end # end
# Render using Tilt # Render using Tilt
content = ::Middleman::Util.instrument 'render.tilt', path: path do content = nil
template.render(context, locs, &block)
end # ::Middleman::Util.instrument 'render.tilt', path: path do
content = template.render(context, locs, &block)
# end
# Allow hooks to manipulate the result after render # Allow hooks to manipulate the result after render
content = @app.callbacks_for(:after_render).reduce(content) do |sum, callback| content = @app.callbacks_for(:after_render).reduce(content) do |sum, callback|

View File

@ -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

View File

@ -127,9 +127,9 @@ module Middleman
# @return [String] # @return [String]
Contract Hash, Hash => String Contract Hash, Hash => String
def render(opts={}, locs={}) 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 md = metadata
opts = md[:options].deep_merge(opts) opts = md[:options].deep_merge(opts)
locs = md[:locals].deep_merge(locs) locs = md[:locals].deep_merge(locs)
@ -140,7 +140,7 @@ module Middleman
renderer = ::Middleman::TemplateRenderer.new(@app, file_descriptor[:full_path].to_s) renderer = ::Middleman::TemplateRenderer.new(@app, file_descriptor[:full_path].to_s)
renderer.render(locs, opts) renderer.render(locs, opts)
end # end
end end
# A path without the directory index - so foo/index.html becomes # A path without the directory index - so foo/index.html becomes

View File

@ -75,6 +75,7 @@ module Middleman
def initialize(app) def initialize(app)
@app = app @app = app
@resources = [] @resources = []
@rebuild_reasons = [:first_run]
@update_count = 0 @update_count = 0
@resource_list_manipulators = ::Hamster::Vector.empty @resource_list_manipulators = ::Hamster::Vector.empty
@ -115,9 +116,10 @@ module Middleman
# Rebuild the list of resources from scratch, using registed manipulators # Rebuild the list of resources from scratch, using registed manipulators
# @return [void] # @return [void]
Contract Maybe[Symbol] => Any Contract Symbol => Any
def rebuild_resource_list!(name=nil) def rebuild_resource_list!(name)
@lock.synchronize do @lock.synchronize do
@rebuild_reasons << name
@app.logger.debug "== Requesting resource list rebuilding: #{name}" @app.logger.debug "== Requesting resource list rebuilding: #{name}"
@needs_sitemap_rebuild = true @needs_sitemap_rebuild = true
end end
@ -198,29 +200,36 @@ module Middleman
def ensure_resource_list_updated! def ensure_resource_list_updated!
@lock.synchronize do @lock.synchronize do
return unless @needs_sitemap_rebuild 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| @resources = []
@app.logger.debug "== Running manipulator: #{m[:name]}"
@resources = m[:manipulator].send(m[:custom_name] || :manipulate_resource_list, @resources)
# Reset lookup cache @resource_list_manipulators.each do |m|
reset_lookup_cache! ::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 # Reset lookup cache
@resources.each do |resource| reset_lookup_cache!
@_lookup_by_path[resource.path] = resource
@_lookup_by_destination_path[resource.destination_path] = resource # 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 end
invalidate_resources_not_ignored_cache! @update_count += 1
end
@update_count += 1 @rebuild_reasons = []
end
end end
end end

View File

@ -213,12 +213,12 @@ module Middleman
# Manually poll all watchers for new content. # Manually poll all watchers for new content.
# #
# @return [void] # @return [void]
Contract Any Contract ArrayOf[Pathname]
def find_new_files! def find_new_files!
return unless @update_count != @last_update_count return [] unless @update_count != @last_update_count
@last_update_count = @update_count @last_update_count = @update_count
watchers.each(&:poll_once!) watchers.reduce([]) { |sum, w| sum + w.poll_once! }
end end
# Start up all listeners. # Start up all listeners.

View File

@ -180,17 +180,19 @@ module Middleman
# Manually trigger update events. # Manually trigger update events.
# #
# @return [void] # @return [void]
Contract Any Contract ArrayOf[Pathname]
def poll_once! def poll_once!
updated = ::Middleman::Util.all_files_under(@directory.to_s) updated = ::Middleman::Util.all_files_under(@directory.to_s)
removed = @files.keys.reject { |p| updated.include?(p) } removed = @files.keys.reject { |p| updated.include?(p) }
update(updated, removed) 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 updated + removed
listen!
end end
# Work around this bug: http://bugs.ruby-lang.org/issues/4521 # Work around this bug: http://bugs.ruby-lang.org/issues/4521

View File

@ -4,22 +4,23 @@ require 'capybara/cucumber'
Given /^a clean server$/ do Given /^a clean server$/ do
@initialize_commands = [] @initialize_commands = []
@activation_commands = []
end end
Given /^"([^\"]*)" feature is "([^\"]*)"$/ do |feature, state| Given /^"([^\"]*)" feature is "([^\"]*)"$/ do |feature, state|
@initialize_commands ||= [] @activation_commands ||= []
if state == 'enabled' if state == 'enabled'
@initialize_commands << lambda { activate(feature.to_sym) } @activation_commands << lambda { activate(feature.to_sym) }
end end
end end
Given /^"([^\"]*)" feature is "enabled" with "([^\"]*)"$/ do |feature, options_str| Given /^"([^\"]*)" feature is "enabled" with "([^\"]*)"$/ do |feature, options_str|
@initialize_commands ||= [] @activation_commands ||= []
options = eval("{#{options_str}}") options = eval("{#{options_str}}")
@initialize_commands << lambda { activate(feature.to_sym, options) } @activation_commands << lambda { activate(feature.to_sym, options) }
end end
Given /^"([^\"]*)" is set to "([^\"]*)"$/ do |variable, value| Given /^"([^\"]*)" is set to "([^\"]*)"$/ do |variable, value|
@ -41,6 +42,7 @@ Given /^the Server is running$/ do
ENV['MM_ROOT'] = root_dir ENV['MM_ROOT'] = root_dir
initialize_commands = @initialize_commands || [] initialize_commands = @initialize_commands || []
activation_commands = @activation_commands || []
@server_inst = ::Middleman::Application.new do @server_inst = ::Middleman::Application.new do
config[:watcher_disable] = true config[:watcher_disable] = true
@ -49,6 +51,12 @@ Given /^the Server is running$/ do
initialize_commands.each do |p| initialize_commands.each do |p|
instance_exec(&p) instance_exec(&p)
end end
app.after_configuration_eval do
activation_commands.each do |p|
config_context.instance_exec(&p)
end
end
end end
Capybara.app = ::Middleman::Rack.new(@server_inst).to_app Capybara.app = ::Middleman::Rack.new(@server_inst).to_app

View File

@ -332,7 +332,7 @@ module Middleman
end end
Contract String, String, ArrayOf[String], Proc => String 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)}))/ matcher = /([=\'\"\(,]\s*)([^\s\'\"\)>]+(#{Regexp.union(exts)}))/
url_fn_prefix = 'url(' url_fn_prefix = 'url('
@ -349,7 +349,7 @@ module Middleman
begin begin
uri = ::Addressable::URI.parse(asset_path) 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}" "#{opening_character}#{result}"
else else
match match

View File

@ -17,7 +17,7 @@ module Middleman
# @return [Array<Hash, String>] # @return [Array<Hash, String>]
Contract Pathname, Maybe[Symbol] => [Hash, Maybe[String]] Contract Pathname, Maybe[Symbol] => [Hash, Maybe[String]]
def parse(full_path, frontmatter_delims, known_type=nil) 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 # Avoid weird race condition when a file is renamed
begin begin
@ -74,8 +74,8 @@ module Middleman
# @return [Hash] # @return [Hash]
Contract String, Pathname, Bool => Hash Contract String, Pathname, Bool => Hash
def parse_yaml(content, full_path) def parse_yaml(content, full_path)
symbolize_recursive(YAML.load(content) || {}) symbolize_recursive(::YAML.load(content) || {})
rescue StandardError, Psych::SyntaxError => error rescue StandardError, ::Psych::SyntaxError => error
warn "YAML Exception parsing #{full_path}: #{error.message}" warn "YAML Exception parsing #{full_path}: #{error.message}"
{} {}
end end
@ -85,7 +85,7 @@ module Middleman
# @return [Hash] # @return [Hash]
Contract String, Pathname => Hash Contract String, Pathname => Hash
def parse_json(content, full_path) def parse_json(content, full_path)
symbolize_recursive(JSON.parse(content) || {}) symbolize_recursive(::JSON.parse(content) || {})
rescue StandardError => error rescue StandardError => error
warn "JSON Exception parsing #{full_path}: #{error.message}" warn "JSON Exception parsing #{full_path}: #{error.message}"
{} {}