diff --git a/CHANGELOG.md b/CHANGELOG.md index 826135e7..7e4e2f4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,9 @@ master === -# Next +# 4.2.0 +* Remove Rack support in favor of `resource.filters << proc { |oldbody| newbody }` * Expose `development?` and `production?` helpers to template context. # 4.1.8 diff --git a/middleman-core/features/asset_hash.feature b/middleman-core/features/asset_hash.feature index 19517e3b..6b92777c 100644 --- a/middleman-core/features/asset_hash.feature +++ b/middleman-core/features/asset_hash.feature @@ -188,23 +188,6 @@ Feature: Assets get file hashes appended to them and references to them are upda When I go to "/partials/" Then I should see 'href="../stylesheets/uses_partials-ec347271.css' - Scenario: The asset hash should change when a Rack-based filter changes - Given a fixture app "asset-hash-app" - And a file named "config.rb" with: - """ - activate :asset_hash - activate :relative_assets - activate :directory_indexes - require 'lib/middleware.rb' - use ::Middleware - """ - Given the Server is running at "asset-hash-app" - When I go to "/" - Then I should see 'href="stylesheets/site-5ad7def0.css' - When I go to "stylesheets/site-5ad7def0.css" - Then I should see 'background-image: url("../images/100px-5fd6fb90.jpg")' - Then I should see 'Added by Rack filter' - Scenario: Hashed-asset files are not produced for ignored paths Given a fixture app "asset-hash-app" And a file named "config.rb" with: diff --git a/middleman-core/features/endpoints.feature b/middleman-core/features/endpoints.feature new file mode 100644 index 00000000..1ff6aaf6 --- /dev/null +++ b/middleman-core/features/endpoints.feature @@ -0,0 +1,17 @@ +Feature: Generic block based pages + + Scenario: Static Ruby Endpoints + Given an empty app + And a file named "config.rb" with: + """ + endpoint "hello.html" do + "world" + end + """ + And a file named "source/index.html.erb" with: + """ + Hi + """ + And the Server is running at "empty_app" + When I go to "/hello.html" + Then I should see "world" diff --git a/middleman-core/features/extension_api_deprecations.feature b/middleman-core/features/extension_api_deprecations.feature index 8c9dd216..15f447ac 100644 --- a/middleman-core/features/extension_api_deprecations.feature +++ b/middleman-core/features/extension_api_deprecations.feature @@ -3,8 +3,9 @@ Feature: Extension author could use some hooks Scenario: When build Given a fixture app "extension-api-deprecations-app" When I run `middleman build` - Then the exit status should be 0 + And the exit status should be 0 And the output should contain "`set :layout` is deprecated" + And the output should contain "Project built successfully" And the file "build/index.html" should contain "In Index" And the file "build/index.html" should not contain "In Layout" diff --git a/middleman-core/features/extension_hooks.feature b/middleman-core/features/extension_hooks.feature index 1dedd9a9..11a05557 100644 --- a/middleman-core/features/extension_hooks.feature +++ b/middleman-core/features/extension_hooks.feature @@ -7,7 +7,6 @@ Feature: Extension author could use some hooks And the output should contain "/// after_configuration ///" And the output should contain "/// ready ///" And the output should contain "/// before_build ///" - And the output should contain "/// before ///" And the output should contain "/// before_render ///" And the output should contain "/// after_render ///" And the output should contain "/// after_build ///" diff --git a/middleman-core/features/mount_rack.feature b/middleman-core/features/mount_rack.feature deleted file mode 100644 index dcb83146..00000000 --- a/middleman-core/features/mount_rack.feature +++ /dev/null @@ -1,65 +0,0 @@ -Feature: Support Rack apps mounted using map - - Scenario: Mounted Rack App at /sinatra - Given the Server is running at "sinatra-app" - When I go to "/" - Then I should see "Hello World (Middleman)" - When I go to "/sinatra/" - Then I should see "Hello World (Sinatra)" - - Scenario: Built Mounted Rack App at /sinatra - Given a successfully built app at "sinatra-app" - When I cd to "build" - Then the following files should exist: - | index.html | - Then the following files should not exist: - | sinatra/index.html | - | sinatra/index2.html | - - Scenario: Static Ruby Endpoints - Given a fixture app "sinatra-app" - And a file named "config.rb" with: - """ - endpoint "hello.html" do - "world" - end - """ - And the Server is running at "sinatra-app" - When I go to "/hello.html" - Then I should see "world" - - Scenario: Built Mounted Rack App at /sinatra (including rack endpoints) - Given a fixture app "sinatra-app" - And a file named "config.rb" with: - """ - require "sinatra" - - class MySinatra < Sinatra::Base - get "/" do - "Hello World (Sinatra)" - end - get "/derp.html" do - "De doo" - end - end - - map "/sinatra" do - run MySinatra - end - - endpoint "sinatra/index2.html", path: "/sinatra/" - - endpoint "dedoo.html", path: "/sinatra/derp.html" - - endpoint "hello.html" do - "world" - end - """ - And a successfully built app at "sinatra-app" - When I cd to "build" - Then the following files should exist: - | index.html | - | sinatra/index2.html | - | dedoo.html | - And the file "sinatra/index2.html" should contain 'Hello World (Sinatra)' - And the file "dedoo.html" should contain 'De doo' \ No newline at end of file diff --git a/middleman-core/fixtures/asset-host-app/source/images/blank11.gif b/middleman-core/fixtures/asset-host-app/source/images/blank11.gif new file mode 100644 index 00000000..e69de29b diff --git a/middleman-core/fixtures/asset-host-app/source/images/blank12.gif b/middleman-core/fixtures/asset-host-app/source/images/blank12.gif new file mode 100644 index 00000000..e69de29b diff --git a/middleman-core/fixtures/asset-host-app/source/images/blank41.gif b/middleman-core/fixtures/asset-host-app/source/images/blank41.gif new file mode 100644 index 00000000..e69de29b diff --git a/middleman-core/fixtures/asset-host-app/source/images/blank42.gif b/middleman-core/fixtures/asset-host-app/source/images/blank42.gif new file mode 100644 index 00000000..e69de29b diff --git a/middleman-core/fixtures/asset-host-app/source/images/blank5.gif b/middleman-core/fixtures/asset-host-app/source/images/blank5.gif new file mode 100644 index 00000000..e69de29b diff --git a/middleman-core/fixtures/asset-host-app/source/images/blank51.gif b/middleman-core/fixtures/asset-host-app/source/images/blank51.gif new file mode 100644 index 00000000..e69de29b diff --git a/middleman-core/fixtures/asset-host-app/source/images/blank52.gif b/middleman-core/fixtures/asset-host-app/source/images/blank52.gif new file mode 100644 index 00000000..e69de29b diff --git a/middleman-core/fixtures/asset-host-app/source/images/blank6.gif b/middleman-core/fixtures/asset-host-app/source/images/blank6.gif new file mode 100644 index 00000000..e69de29b diff --git a/middleman-core/fixtures/asset-host-app/source/images/blank61.gif b/middleman-core/fixtures/asset-host-app/source/images/blank61.gif new file mode 100644 index 00000000..e69de29b diff --git a/middleman-core/fixtures/asset-host-app/source/images/blank62.gif b/middleman-core/fixtures/asset-host-app/source/images/blank62.gif new file mode 100644 index 00000000..e69de29b diff --git a/middleman-core/fixtures/asset-host-app/source/images/blank7.gif b/middleman-core/fixtures/asset-host-app/source/images/blank7.gif new file mode 100644 index 00000000..e69de29b diff --git a/middleman-core/fixtures/asset-host-app/source/images/blank71.gif b/middleman-core/fixtures/asset-host-app/source/images/blank71.gif new file mode 100644 index 00000000..e69de29b diff --git a/middleman-core/fixtures/asset-host-app/source/images/blank72.gif b/middleman-core/fixtures/asset-host-app/source/images/blank72.gif new file mode 100644 index 00000000..e69de29b diff --git a/middleman-core/fixtures/asset-host-app/source/images/blank8.gif b/middleman-core/fixtures/asset-host-app/source/images/blank8.gif new file mode 100644 index 00000000..e69de29b diff --git a/middleman-core/fixtures/asset-host-app/source/images/blank81.gif b/middleman-core/fixtures/asset-host-app/source/images/blank81.gif new file mode 100644 index 00000000..e69de29b diff --git a/middleman-core/fixtures/asset-host-app/source/images/blank82.gif b/middleman-core/fixtures/asset-host-app/source/images/blank82.gif new file mode 100644 index 00000000..e69de29b diff --git a/middleman-core/fixtures/sinatra-app/config.rb b/middleman-core/fixtures/sinatra-app/config.rb deleted file mode 100644 index 0251a715..00000000 --- a/middleman-core/fixtures/sinatra-app/config.rb +++ /dev/null @@ -1,14 +0,0 @@ -require "sinatra" - -class MySinatra < Sinatra::Base - get "/" do - "Hello World (Sinatra)" - end - get "/derp.html" do - "De doo" - end -end - -map "/sinatra" do - run MySinatra -end diff --git a/middleman-core/fixtures/sinatra-app/source/index.html.erb b/middleman-core/fixtures/sinatra-app/source/index.html.erb deleted file mode 100644 index 8b59c41c..00000000 --- a/middleman-core/fixtures/sinatra-app/source/index.html.erb +++ /dev/null @@ -1,5 +0,0 @@ ---- -layout: false ---- - -Hello World (Middleman) \ No newline at end of file diff --git a/middleman-core/lib/middleman-core/application.rb b/middleman-core/lib/middleman-core/application.rb index 379d1e81..4ecd40f8 100644 --- a/middleman-core/lib/middleman-core/application.rb +++ b/middleman-core/lib/middleman-core/application.rb @@ -157,12 +157,12 @@ module Middleman define_setting :layout, :_auto_layout, 'Default layout name' # Which file extensions have a layout by default. - # @return [Array.] - define_setting :extensions_with_layout, %w(.htm .html .xhtml .php), 'Which file extensions have a layout by default.' + # @return [Set.] + define_setting :extensions_with_layout, %w(.htm .html .xhtml .php), 'Which file extensions have a layout by default.', set: true # Which file extensions are "assets." # @return [Array.] - define_setting :asset_extensions, %w(.css .png .jpg .jpeg .webp .svg .svgz .js .gif .ttf .otf .woff .woff2 .eot .ico .map), 'Which file extensions are treated as assets.' + define_setting :asset_extensions, %w(.css .png .jpg .jpeg .webp .svg .svgz .js .gif .ttf .otf .woff .woff2 .eot .ico .map), 'Which file extensions are treated as assets.', set: true # Default string encoding for templates and output. # @return [String] diff --git a/middleman-core/lib/middleman-core/builder.rb b/middleman-core/lib/middleman-core/builder.rb index 1796d4d7..c84bfa39 100644 --- a/middleman-core/lib/middleman-core/builder.rb +++ b/middleman-core/lib/middleman-core/builder.rb @@ -2,7 +2,6 @@ require 'pathname' require 'fileutils' require 'tempfile' require 'parallel' -require 'middleman-core/rack' require 'middleman-core/callback_manager' require 'middleman-core/contracts' @@ -39,9 +38,6 @@ module Middleman @cleaning = opts.fetch(:clean) @parallel = opts.fetch(:parallel, true) - rack_app = ::Middleman::Rack.new(@app).to_app - @rack = ::Rack::MockRequest.new(rack_app) - @callbacks = ::Middleman::CallbackManager.new @callbacks.install_methods!(self, [:on_build_event]) end @@ -227,15 +223,7 @@ module Middleman if resource.binary? export_file!(output_file, resource.file_descriptor[:full_path]) else - response = @rack.get(::URI.escape(resource.request_path)) - - # If we get a response, save it to a tempfile. - if response.status == 200 - export_file!(output_file, binary_encode(response.body)) - else - trigger(:error, output_file, response.body) - return false - end + export_file!(output_file, binary_encode(resource.render)) end rescue => e trigger(:error, output_file, "#{e}\n#{e.backtrace.join("\n")}") diff --git a/middleman-core/lib/middleman-core/configuration.rb b/middleman-core/lib/middleman-core/configuration.rb index c916fc90..0d6d9be7 100644 --- a/middleman-core/lib/middleman-core/configuration.rb +++ b/middleman-core/lib/middleman-core/configuration.rb @@ -1,3 +1,5 @@ +require 'set' + module Middleman module Configuration # A class that manages a collection of documented settings. @@ -129,23 +131,37 @@ module Middleman def initialize(key, default, description, options={}) @value_set = false + @array_wrapped_value = nil + @array_wrapped_default = nil self.key = key self.default = default self.description = description self.options = options + + @array_wrapped_default = if self.default && options[:set] && self.default.is_a?(Array) + Set.new(self.default) + end end # The user-supplied value for this setting, overriding the default def value=(value) @value = value @value_set = true + + @array_wrapped_value = if @value && options[:set] && @value.is_a?(Array) + Set.new(@value) + end end # The effective value of the setting, which may be the default # if the user has not set a value themselves. Note that even if the # user sets the value to nil it will override the default. def value - value_set? ? @value : default + if value_set? + @array_wrapped_value ? @array_wrapped_value : @value + else + @array_wrapped_default ? @array_wrapped_default : default + end end # Whether or not there has been a value set beyond the default diff --git a/middleman-core/lib/middleman-core/core_extensions.rb b/middleman-core/lib/middleman-core/core_extensions.rb index e38be937..ea31f89a 100644 --- a/middleman-core/lib/middleman-core/core_extensions.rb +++ b/middleman-core/lib/middleman-core/core_extensions.rb @@ -19,12 +19,6 @@ 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/inline_url_rewriter.rb b/middleman-core/lib/middleman-core/core_extensions/inline_url_rewriter.rb deleted file mode 100644 index 43354e97..00000000 --- a/middleman-core/lib/middleman-core/core_extensions/inline_url_rewriter.rb +++ /dev/null @@ -1,135 +0,0 @@ -require 'rack' -require 'rack/response' -require 'memoist' -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 - - REWRITER_DESCRIPTOR = { - id: Symbol, - proc: Or[Proc, Method], - url_extensions: ArrayOf[String], - source_extensions: ArrayOf[String], - ignore: ArrayOf[::Middleman::Util::IGNORE_DESCRIPTOR], - after: Maybe[Symbol] - }.freeze - - def initialize(app, options_hash={}, &block) - super - - @rewriters = {} - end - - Contract REWRITER_DESCRIPTOR => Any - def add(options) - @rewriters[options] = options - end - - def after_configuration - return if @rewriters.empty? - - rewriters = @rewriters.values.sort do |a, b| - if b[:after] && b[:after] == a[:id] - 1 - else - 0 - end - end - - app.use Rack, rewriters: rewriters, middleman_app: @app - end - - class Rack - extend Memoist - 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) - - 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) - 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' - - 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 = ::Middleman::Util.instrument 'inline_url_rewriter', path: path do - ::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? - - 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| ::Middleman::Util.should_ignore?(r, full_asset_path) } - - rewrite_ignore = Array(rewriter[:rewrite_ignore] || []) - next if rewrite_ignore.any? { |i| ::Middleman::Util.path_match(i, 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 - end - end - end -end diff --git a/middleman-core/lib/middleman-core/extension.rb b/middleman-core/lib/middleman-core/extension.rb index 9805455a..0d1f212d 100644 --- a/middleman-core/lib/middleman-core/extension.rb +++ b/middleman-core/lib/middleman-core/extension.rb @@ -60,7 +60,7 @@ module Middleman # There are also some less common hooks that can be listened to from within an extension's `initialize` method: # # * `app.before_render {|body, path, locs, template_class| ... }` - Manipulate template sources before they are rendered. - # * `app.after_render {|content, path, locs, template_class| ... }` - Manipulate output text after a template has been rendered. It is also common to install a Rack middleware to do this instead. + # * `app.after_render {|content, path, locs, template_class| ... }` - Manipulate output text after a template has been rendered. # * `app.ready { ... }` - Run code once Middleman is ready to serve or build files (after `after_configuration`). # diff --git a/middleman-core/lib/middleman-core/extensions/asset_hash.rb b/middleman-core/lib/middleman-core/extensions/asset_hash.rb index b0b955f9..f78eed5e 100644 --- a/middleman-core/lib/middleman-core/extensions/asset_hash.rb +++ b/middleman-core/lib/middleman-core/extensions/asset_hash.rb @@ -1,18 +1,15 @@ require 'middleman-core/util' -require 'middleman-core/rack' class Middleman::Extensions::AssetHash < ::Middleman::Extension - option :sources, %w(.css .htm .html .js .php .xhtml), 'List of extensions that are searched for hashable assets.' - option :exts, nil, 'List of extensions that get asset hashes appended to them.' + option :sources, %w(.css .htm .html .js .php .xhtml), 'List of extensions that are searched for hashable assets.', set: true + option :exts, nil, 'List of extensions that get asset hashes appended to them.', set: true option :ignore, [], 'Regexes of filenames to skip adding asset hashes to' option :rewrite_ignore, [], 'Regexes of filenames to skip processing for path rewrites' def initialize(app, options_hash={}, &block) super - require 'addressable/uri' require 'digest/sha1' - require 'rack/mock' # Allow specifying regexes to ignore, plus always ignore apple touch icons @ignore = Array(options.ignore) + [/^apple-touch-icon/] @@ -20,14 +17,6 @@ class Middleman::Extensions::AssetHash < ::Middleman::Extension # Exclude .ico from the default list because browsers expect it # to be named "favicon.ico" @exts = options.exts || (app.config[:asset_extensions] - %w(.ico)) - - app.rewrite_inline_urls id: :asset_hash, - url_extensions: @exts.sort.reverse, - source_extensions: options.sources, - ignore: @ignore, - rewrite_ignore: options.rewrite_ignore, - proc: method(:rewrite_url), - after: :asset_host end Contract String, Or[String, Pathname], Any => Maybe[String] @@ -45,7 +34,6 @@ class Middleman::Extensions::AssetHash < ::Middleman::Extension replacement_path = "/#{asset_page.destination_path}" replacement_path = Pathname.new(replacement_path).relative_path_from(dirpath).to_s if relative_path - replacement_path end @@ -53,9 +41,18 @@ class Middleman::Extensions::AssetHash < ::Middleman::Extension # @return Array Contract ResourceList => ResourceList def manipulate_resource_list(resources) - @rack_client ||= begin - rack_app = ::Middleman::Rack.new(app).to_app - ::Rack::MockRequest.new(rack_app) + resources.each do |r| + next unless r.destination_path.end_with?('/', *options.sources) + next if Array(options.rewrite_ignore || []).any? do |i| + ::Middleman::Util.path_match(i, "/#{r.destination_path}") + end + + r.filters << ::Middleman::InlineURLRewriter.new(:asset_hash, + app, + r, + url_extensions: @exts, + ignore: options.ignore, + proc: method(:rewrite_url)) end # Process resources in order: binary images and fonts, then SVG, then JS/CSS. @@ -81,15 +78,9 @@ class Middleman::Extensions::AssetHash < ::Middleman::Extension digest = if resource.binary? ::Digest::SHA1.file(resource.source_file).hexdigest[0..7] else - # 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 - - ::Digest::SHA1.hexdigest(response.body)[0..7] + # Render without asset hash + body = resource.render { |f| !f.respond_to?(:filter_name) || f.filter_name != :asset_hash } + ::Digest::SHA1.hexdigest(body)[0..7] end resource.destination_path = resource.destination_path.sub(/\.(\w+)$/) { |ext| "-#{digest}#{ext}" } diff --git a/middleman-core/lib/middleman-core/extensions/asset_host.rb b/middleman-core/lib/middleman-core/extensions/asset_host.rb index 7cb83d07..b076299d 100644 --- a/middleman-core/lib/middleman-core/extensions/asset_host.rb +++ b/middleman-core/lib/middleman-core/extensions/asset_host.rb @@ -1,5 +1,3 @@ -require 'addressable/uri' - class Middleman::Extensions::AssetHost < ::Middleman::Extension option :host, nil, 'The asset host to use or a Proc to determine asset host', required: true option :exts, nil, 'List of extensions that get cache busters strings appended to them.' @@ -7,15 +5,22 @@ 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 initialize(app, options_hash={}, &block) - super + Contract ResourceList => ResourceList + def manipulate_resource_list(resources) + resources.each do |r| + next unless r.destination_path.end_with?('/', *options.sources) + next if Array(options.rewrite_ignore || []).any? do |i| + ::Middleman::Util.path_match(i, "/#{r.destination_path}") + end - app.rewrite_inline_urls id: :asset_host, - url_extensions: options.exts || app.config[:asset_extensions], - source_extensions: options.sources, - ignore: options.ignore, - rewrite_ignore: options.rewrite_ignore, - proc: method(:rewrite_url) + r.filters << ::Middleman::InlineURLRewriter.new(:asset_host, + app, + r, + after_filter: :asset_hash, + url_extensions: options.exts || app.config[:asset_extensions], + ignore: options.ignore, + proc: method(:rewrite_url)) + end 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 2de11632..ea723a81 100644 --- a/middleman-core/lib/middleman-core/extensions/cache_buster.rb +++ b/middleman-core/lib/middleman-core/extensions/cache_buster.rb @@ -1,19 +1,25 @@ # The Cache Buster extension class Middleman::Extensions::CacheBuster < ::Middleman::Extension - option :exts, nil, 'List of extensions that get cache busters strings appended to them.' - option :sources, %w(.css .htm .html .js .php .xhtml), 'List of extensions that are searched for bustable assets.' + option :exts, nil, 'List of extensions that get cache busters strings appended to them.', set: true + option :sources, %w(.css .htm .html .js .php .xhtml), 'List of extensions that are searched for bustable assets.', set: true option :ignore, [], 'Regexes of filenames to skip adding query strings to' option :rewrite_ignore, [], 'Regexes of filenames to skip processing for path rewrites' - def initialize(app, options_hash={}, &block) - super + Contract ResourceList => ResourceList + def manipulate_resource_list(resources) + resources.each do |r| + next unless r.destination_path.end_with?('/', *options.sources) + next if Array(options.rewrite_ignore || []).any? do |i| + ::Middleman::Util.path_match(i, "/#{r.destination_path}") + end - app.rewrite_inline_urls id: :cache_buster, - url_extensions: options.exts || app.config[:asset_extensions], - source_extensions: options.sources, - ignore: options.ignore, - rewrite_ignore: options.rewrite_ignore, - proc: method(:rewrite_url) + r.filters << ::Middleman::InlineURLRewriter.new(:cache_buster, + app, + r, + url_extensions: options.exts || app.config[:asset_extensions], + ignore: options.ignore, + proc: method(:rewrite_url)) + end end Contract String, Or[String, Pathname], Any => String diff --git a/middleman-core/lib/middleman-core/extensions/directory_indexes.rb b/middleman-core/lib/middleman-core/extensions/directory_indexes.rb index cef88868..3bc340ee 100644 --- a/middleman-core/lib/middleman-core/extensions/directory_indexes.rb +++ b/middleman-core/lib/middleman-core/extensions/directory_indexes.rb @@ -1,3 +1,5 @@ +require 'set' + # Directory Indexes extension class Middleman::Extensions::DirectoryIndexes < ::Middleman::Extension # This should run after most other sitemap manipulators so that it @@ -11,7 +13,7 @@ class Middleman::Extensions::DirectoryIndexes < ::Middleman::Extension index_file = app.config[:index_file] new_index_path = "/#{index_file}" - extensions = %w(.htm .html .php .xhtml) + extensions = Set.new(%w(.htm .html .php .xhtml)) resources.each do |resource| # Check if it would be pointless to reroute diff --git a/middleman-core/lib/middleman-core/extensions/gzip.rb b/middleman-core/lib/middleman-core/extensions/gzip.rb index 0483fe32..a633f78f 100644 --- a/middleman-core/lib/middleman-core/extensions/gzip.rb +++ b/middleman-core/lib/middleman-core/extensions/gzip.rb @@ -10,7 +10,7 @@ # to .css, .htm, .html, .js, and .xhtml # class Middleman::Extensions::Gzip < ::Middleman::Extension - option :exts, %w(.css .htm .html .js .svg .xhtml), 'File extensions to Gzip when building.' + option :exts, %w(.css .htm .html .js .svg .xhtml), 'File extensions to Gzip when building.', set: true option :ignore, [], 'Patterns to avoid gzipping' option :overwrite, false, 'Overwrite original files instead of adding .gz extension.' diff --git a/middleman-core/lib/middleman-core/extensions/minify_css.rb b/middleman-core/lib/middleman-core/extensions/minify_css.rb index 067b25bf..9174322f 100644 --- a/middleman-core/lib/middleman-core/extensions/minify_css.rb +++ b/middleman-core/lib/middleman-core/extensions/minify_css.rb @@ -1,5 +1,5 @@ -require 'memoist' require 'middleman-core/contracts' +require 'rack/mime' # Minify CSS Extension class Middleman::Extensions::MinifyCss < ::Middleman::Extension @@ -9,17 +9,10 @@ class Middleman::Extensions::MinifyCss < ::Middleman::Extension require 'sass' SassCompressor }, 'Set the CSS compressor to use.' - option :content_types, %w(text/css), 'Content types of resources that contain CSS' - option :inline_content_types, %w(text/html text/php), 'Content types of resources that contain inline CSS' + option :content_types, %w(text/css), 'Content types of resources that contain CSS', set: true + option :inline_content_types, %w(text/html text/php), 'Content types of resources that contain inline CSS', set: true - def ready - # Setup Rack middleware to minify CSS - app.use Rack, compressor: options[:compressor], - ignore: Array(options[:ignore]) + [/\.min\./], - inline: options[:inline], - content_types: options[:content_types], - inline_content_types: options[:inline_content_types] - end + INLINE_CSS_REGEX = /(]*>\s*(?:\/\*\*\/)?\s*<\/style>)/m class SassCompressor def self.compress(style, options={}) @@ -29,97 +22,61 @@ class Middleman::Extensions::MinifyCss < ::Middleman::Extension end end - # Rack middleware to look for CSS and compress it - class Rack - extend Memoist - include Contracts - INLINE_CSS_REGEX = /(]*>\s*(?:\/\*\*\/)?\s*<\/style>)/m + def initialize(app, options_hash={}, &block) + super - # Init - # @param [Class] app - # @param [Hash] options - Contract RespondTo[:call], { - ignore: ArrayOf[PATH_MATCHER], - inline: Bool, - compressor: Or[Proc, RespondTo[:to_proc], RespondTo[:compress]] - } => Any - def initialize(app, options={}) - @app = app - @ignore = options.fetch(:ignore) - @inline = options.fetch(:inline) - - @compressor = options.fetch(:compressor) - @compressor = @compressor.to_proc if @compressor.respond_to? :to_proc - @compressor = @compressor.call if @compressor.is_a? Proc - @content_types = options[:content_types] - @inline_content_types = options[:inline_content_types] - end - - # Rack interface - # @param [Rack::Environmemt] env - # @return [Array] - def call(env) - status, headers, response = @app.call(env) - - content_type = headers['Content-Type'].try(:slice, /^[^;]*/) - path = env['PATH_INFO'] - - minified = if @inline && minifiable_inline?(content_type) - minify_inline(::Middleman::Util.extract_response_text(response)) - elsif minifiable?(content_type) && !ignore?(path) - minify(::Middleman::Util.extract_response_text(response)) - end - - if minified - headers['Content-Length'] = ::Rack::Utils.bytesize(minified).to_s - response = [minified] - end - - [status, headers, response] - end - - private - - # Whether the path should be ignored - # @param [String] path - # @return [Boolean] - 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 - # @return [Boolean] - 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 - # @return [Boolean] - def minifiable_inline?(content_type) - @inline_content_types.include?(content_type) - end - memoize :minifiable_inline? - - # Minify the content - # @param [String] content - # @return [String] - def minify(content) - @compressor.compress(content) - end - memoize :minify - - # Detect and minify inline content - # @param [String] content - # @return [String] - def minify_inline(content) - content.gsub(INLINE_CSS_REGEX) do - $1 + minify($2) + $3 - end - end - memoize :minify_inline + @ignore = Array(options[:ignore]) + [/\.min\./] + @compressor = options[:compressor] + @compressor = @compressor.to_proc if @compressor.respond_to? :to_proc + @compressor = @compressor.call if @compressor.is_a? Proc end + + Contract ResourceList => ResourceList + def manipulate_resource_list(resources) + resources.each do |r| + type = r.content_type.try(:slice, /^[^;]*/) + if options[:inline] && minifiable_inline?(type) + r.filters << method(:minify_inline) + elsif minifiable?(type) && !ignore?(r.destination_path) + r.filters << method(:minify) + end + end + end + + # Whether the path should be ignored + Contract String => Bool + def ignore?(path) + @ignore.any? { |ignore| ::Middleman::Util.path_match(ignore, path) } + end + memoize :ignore? + + # Whether this type of content can be minified + Contract Maybe[String] => Bool + def minifiable?(content_type) + options[:content_types].include?(content_type) + end + memoize :minifiable? + + # Whether this type of content contains inline content that can be minified + Contract Maybe[String] => Bool + def minifiable_inline?(content_type) + options[:inline_content_types].include?(content_type) + end + memoize :minifiable_inline? + + # Minify the content + Contract String => String + def minify(content) + @compressor.compress(content) + end + memoize :minify + + # Detect and minify inline content + Contract String => String + def minify_inline(content) + content.gsub(INLINE_CSS_REGEX) do + $1 + minify($2) + $3 + end + end + memoize :minify_inline end diff --git a/middleman-core/lib/middleman-core/extensions/minify_javascript.rb b/middleman-core/lib/middleman-core/extensions/minify_javascript.rb index 48490470..02172e91 100644 --- a/middleman-core/lib/middleman-core/extensions/minify_javascript.rb +++ b/middleman-core/lib/middleman-core/extensions/minify_javascript.rb @@ -9,122 +9,79 @@ class Middleman::Extensions::MinifyJavascript < ::Middleman::Extension require 'uglifier' ::Uglifier.new }, 'Set the JS compressor to use.' - option :content_types, %w(application/javascript), 'Content types of resources that contain JS' - option :inline_content_types, %w(text/html text/php), 'Content types of resources that contain inline JS' + option :content_types, %w(application/javascript), 'Content types of resources that contain JS', set: true + option :inline_content_types, %w(text/html text/php), 'Content types of resources that contain inline JS', set: true - def ready - # Setup Rack middleware to minify JS - app.use Rack, compressor: options[:compressor], - ignore: Array(options[:ignore]) + [/\.min\./], - inline: options[:inline], - content_types: options[:content_types], - inline_content_types: options[:inline_content_types] + INLINE_JS_REGEX = /(]*>\s*(?:\/\/(?:(?:)|(?:\]\]>)))?\s*<\/script>)/m + + def initialize(app, options_hash={}, &block) + super + + @ignore = Array(options[:ignore]) + [/\.min\./] + @compressor = options[:compressor] + @compressor = @compressor.to_proc if @compressor.respond_to? :to_proc + @compressor = @compressor.call if @compressor.is_a? Proc end - # Rack middleware to look for JS and compress it - class Rack - extend Memoist - include Contracts - INLINE_JS_REGEX = /(]*>\s*(?:\/\/(?:(?:)|(?:\]\]>)))?\s*<\/script>)/m - - # Init - # @param [Class] app - # @param [Hash] options - Contract RespondTo[:call], { - ignore: ArrayOf[PATH_MATCHER], - inline: Bool, - compressor: Or[Proc, RespondTo[:to_proc], RespondTo[:compress]] - } => Any - def initialize(app, options={}) - @app = app - @ignore = options.fetch(:ignore) - @inline = options.fetch(:inline) - - @compressor = options.fetch(:compressor) - @compressor = @compressor.to_proc if @compressor.respond_to? :to_proc - @compressor = @compressor.call if @compressor.is_a? Proc - @content_types = options[:content_types] - @inline_content_types = options[:inline_content_types] - end - - # Rack interface - # @param [Rack::Environmemt] env - # @return [Array] - def call(env) - status, headers, response = @app.call(env) - - type = headers['Content-Type'].try(:slice, /^[^;]*/) - @path = env['PATH_INFO'] - - minified = if @inline && minifiable_inline?(type) - minify_inline(::Middleman::Util.extract_response_text(response)) - elsif minifiable?(type) && !ignore?(@path) - minify(::Middleman::Util.extract_response_text(response)) - end - - if minified - headers['Content-Length'] = ::Rack::Utils.bytesize(minified).to_s - response = [minified] - end - - [status, headers, response] - end - - private - - # Whether the path should be ignored - # @param [String] path - # @return [Boolean] - 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 - # @return [Boolean] - 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 - # @return [Boolean] - def minifiable_inline?(content_type) - @inline_content_types.include?(content_type) - end - memoize :minifiable_inline? - - # Minify the content - # @param [String] content - # @return [String] - def minify(content) - @compressor.compress(content) - rescue ExecJS::ProgramError => e - warn "WARNING: Couldn't compress JavaScript in #{@path}: #{e.message}" - content - end - memoize :minify - - # Detect and minify inline content - # @param [String] content - # @return [String] - def minify_inline(content) - content.gsub(INLINE_JS_REGEX) do |match| - first = $1 - inline_content = $2 - last = $3 - - # Only compress script tags that contain JavaScript (as opposed to - # something like jQuery templates, identified with a "text/html" type). - if !first.include?('type=') || first.include?('text/javascript') - first + minify(inline_content) + last - else - match - end + Contract ResourceList => ResourceList + def manipulate_resource_list(resources) + resources.each do |r| + type = r.content_type.try(:slice, /^[^;]*/) + if options[:inline] && minifiable_inline?(type) + r.filters << method(:minify_inline) + elsif minifiable?(type) && !ignore?(r.destination_path) + r.filters << method(:minify) end end - memoize :minify_inline end + + # Whether the path should be ignored + Contract String => Bool + def ignore?(path) + @ignore.any? { |ignore| ::Middleman::Util.path_match(ignore, path) } + end + memoize :ignore? + + # Whether this type of content can be minified + Contract Maybe[String] => Bool + def minifiable?(content_type) + options[:content_types].include?(content_type) + end + memoize :minifiable? + + # Whether this type of content contains inline content that can be minified + Contract Maybe[String] => Bool + def minifiable_inline?(content_type) + options[:inline_content_types].include?(content_type) + end + memoize :minifiable_inline? + + # Minify the content + Contract String => String + def minify(content) + @compressor.compress(content) + rescue ::ExecJS::ProgramError => e + warn "WARNING: Couldn't compress JavaScript in #{@path}: #{e.message}" + content + end + memoize :minify + + # Detect and minify inline content + Contract String => String + def minify_inline(content) + content.gsub(INLINE_JS_REGEX) do |match| + first = $1 + inline_content = $2 + last = $3 + + # Only compress script tags that contain JavaScript (as opposed to + # something like jQuery templates, identified with a "text/html" type). + if !first.include?('type=') || first.include?('text/javascript') + first + minify(inline_content) + last + else + match + end + end + end + memoize :minify_inline end diff --git a/middleman-core/lib/middleman-core/extensions/relative_assets.rb b/middleman-core/lib/middleman-core/extensions/relative_assets.rb index 853a7005..4d2e15db 100644 --- a/middleman-core/lib/middleman-core/extensions/relative_assets.rb +++ b/middleman-core/lib/middleman-core/extensions/relative_assets.rb @@ -1,26 +1,28 @@ -require 'addressable/uri' - # Relative Assets extension class Middleman::Extensions::RelativeAssets < ::Middleman::Extension - option :exts, nil, 'List of extensions that get converted to relative paths.' - option :sources, %w(.css .htm .html .xhtml), 'List of extensions that are searched for relative assets.' + option :exts, nil, 'List of extensions that get converted to relative paths.', set: true + option :sources, %w(.css .htm .html .xhtml), 'List of extensions that are searched for relative assets.', set: true option :ignore, [], 'Regexes of filenames to skip converting to relative paths.' option :rewrite_ignore, [], 'Regexes of filenames to skip processing for path rewrites.' option :helpers_only, false, 'Allow only Ruby helpers to change paths.' - def initialize(app, options_hash={}, &block) - super + Contract ResourceList => ResourceList + def manipulate_resource_list(resources) + return resources if options[:helpers_only] - if options[:helpers_only] - return + resources.each do |r| + next unless r.destination_path.end_with?('/', *options.sources) + next if Array(options.rewrite_ignore || []).any? do |i| + ::Middleman::Util.path_match(i, "/#{r.destination_path}") + end + + r.filters << ::Middleman::InlineURLRewriter.new(:relative_assets, + app, + r, + url_extensions: options.exts || app.config[:asset_extensions], + ignore: options.ignore, + proc: method(:rewrite_url)) end - - app.rewrite_inline_urls id: :relative_assets, - url_extensions: options.exts || app.config[:asset_extensions], - source_extensions: options.sources, - ignore: options.ignore, - rewrite_ignore: options.rewrite_ignore, - proc: method(:rewrite_url) end def mark_as_relative(file_path, opts, current_resource) @@ -53,7 +55,7 @@ class Middleman::Extensions::RelativeAssets < ::Middleman::Extension end def asset_path(kind, source, options={}) - super(kind, source, app.extensions[:relative_assets].mark_as_relative(super, options, current_resource)) + super(kind, source, app.extensions[:relative_assets].mark_as_relative(super, options, current_resource)) end end diff --git a/middleman-core/lib/middleman-core/inline_url_rewriter.rb b/middleman-core/lib/middleman-core/inline_url_rewriter.rb new file mode 100644 index 00000000..ae572313 --- /dev/null +++ b/middleman-core/lib/middleman-core/inline_url_rewriter.rb @@ -0,0 +1,49 @@ +require 'middleman-core/util' +require 'middleman-core/contracts' + +module Middleman + class InlineURLRewriter + include Contracts + + attr_reader :filter_name + attr_reader :after_filter + + def initialize(filter_name, app, resource, options={}) + @filter_name = filter_name + @app = app + @resource = resource + @options = options + + @after_filter = @options.fetch(:after_filter, nil) + end + + Contract String => String + def execute_filter(body) + path = "/#{@resource.destination_path}" + dirpath = ::Pathname.new(File.dirname(path)) + + ::Middleman::Util.instrument 'inline_url_rewriter', path: path do + ::Middleman::Util.rewrite_paths(body, path, @options.fetch(:url_extensions), @app) do |asset_path| + uri = ::Middleman::Util.parse_uri(asset_path) + + relative_path = uri.host.nil? + full_asset_path = if relative_path + dirpath.join(asset_path).to_s + else + asset_path + end + + exts = @options.fetch(:url_extensions) + next unless exts.include?(::File.extname(asset_path)) + + next if @options.fetch(:ignore).any? { |r| ::Middleman::Util.should_ignore?(r, full_asset_path) } + + result = @options.fetch(:proc).call(asset_path, dirpath, path) + asset_path = result if result + + asset_path + end + end + end + end +end diff --git a/middleman-core/lib/middleman-core/rack.rb b/middleman-core/lib/middleman-core/rack.rb index 293a2e43..797d3bf4 100644 --- a/middleman-core/lib/middleman-core/rack.rb +++ b/middleman-core/lib/middleman-core/rack.rb @@ -93,9 +93,6 @@ module Middleman request_path = ::Middleman::Util.full_path(request_path, @middleman) full_request_path = File.join(env['SCRIPT_NAME'], request_path) # Path including rack mount - # Run before callbacks - @middleman.execute_callbacks(:before) - # Get the resource object for this path resource = @middleman.sitemap.find_resource_by_destination_path(request_path.gsub(' ', '%20')) diff --git a/middleman-core/lib/middleman-core/sitemap/extensions/request_endpoints.rb b/middleman-core/lib/middleman-core/sitemap/extensions/request_endpoints.rb index ede1cc64..85601960 100644 --- a/middleman-core/lib/middleman-core/sitemap/extensions/request_endpoints.rb +++ b/middleman-core/lib/middleman-core/sitemap/extensions/request_endpoints.rb @@ -9,14 +9,13 @@ module Middleman # Expose `endpoint` expose_to_config :endpoint - EndpointDescriptor = Struct.new(:path, :request_path, :block) do + EndpointDescriptor = Struct.new(:path, :block) do def execute_descriptor(app, resources) - r = EndpointResource.new( + r = ::Middleman::Sitemap::CallbackResource.new( app.sitemap, path, - request_path + &block ) - r.output = block if block resources + [r] end @@ -24,43 +23,10 @@ module Middleman # Setup a proxy from a path to a target # @param [String] path - # @param [Hash] opts The :path value gives a request path if it # differs from the output path - Contract String, Or[{ path: String }, Proc] => EndpointDescriptor - def endpoint(path, opts={}, &block) - if block_given? - EndpointDescriptor.new(path, path, block) - else - EndpointDescriptor.new(path, opts[:path] || path, nil) - end - end - end - - class EndpointResource < ::Middleman::Sitemap::Resource - Contract Maybe[Proc] - attr_accessor :output - - def initialize(store, path, request_path) - super(store, path) - @request_path = ::Middleman::Util.normalize_path(request_path) - end - - Contract String - attr_reader :request_path - - Contract Bool - def template? - true - end - - Contract Args[Any] => String - def render(*) - return output.call if output - end - - Contract Bool - def ignored? - false + Contract String, Proc => EndpointDescriptor + def endpoint(path, &block) + EndpointDescriptor.new(path, block) end end end diff --git a/middleman-core/lib/middleman-core/sitemap/resource.rb b/middleman-core/lib/middleman-core/sitemap/resource.rb index ce1bc100..f4a33f83 100644 --- a/middleman-core/lib/middleman-core/sitemap/resource.rb +++ b/middleman-core/lib/middleman-core/sitemap/resource.rb @@ -3,6 +3,7 @@ require 'middleman-core/sitemap/extensions/traversal' require 'middleman-core/file_renderer' require 'middleman-core/template_renderer' require 'middleman-core/contracts' +require 'middleman-core/inline_url_rewriter' module Middleman # Sitemap namespace @@ -39,24 +40,26 @@ module Middleman attr_reader :metadata attr_accessor :ignored + attr_accessor :filters # Initialize resource with parent store and URL # @param [Middleman::Sitemap::Store] store # @param [String] path # @param [String] source - Contract IsA['Middleman::Sitemap::Store'], String, Maybe[Or[IsA['Middleman::SourceFile'], String]] => Any - def initialize(store, path, source=nil) + Contract IsA['Middleman::Sitemap::Store'], String, Maybe[Any] => Any + def initialize(store, path, source_file=nil) @store = store @app = @store.app @path = path @ignored = false + @filters = [] - source = Pathname(source) if source && source.is_a?(String) + source_file = Pathname(source_file) if source_file && source_file.is_a?(String) - @file_descriptor = if source && source.is_a?(Pathname) - ::Middleman::SourceFile.new(source.relative_path_from(@app.source_dir), source, @app.source_dir, Set.new([:source]), 0) + @file_descriptor = if source_file && source_file.is_a?(Pathname) + ::Middleman::SourceFile.new(source_file.relative_path_from(@app.source_dir), source_file, @app.source_dir, Set.new([:source]), 0) else - source + source_file end @destination_path = @path @@ -138,8 +141,45 @@ module Middleman # Render this resource # @return [String] - Contract Hash, Hash => String + # Contract Maybe[Hash], Maybe[Hash], Maybe[Proc] => String def render(opts={}, locs={}) + body = render_without_filters(opts, locs) + + return body if @filters.empty? + + sortable_filters = @filters.select { |f| f.respond_to?(:filter_name) }.sort do |a, b| + if b.after_filter == a.filter_name + 1 + else + -1 + end + end.reverse + + n = 0 + sorted_filters = @filters.sort_by do |m| + n += 1 + idx = sortable_filters.index(m) + + [idx.nil? ? 0 : idx, n] + end + + sorted_filters.reduce(body) do |output, filter| + if block_given? && !yield(filter) + output + elsif filter.respond_to?(:execute_filter) + filter.execute_filter(output) + elsif filter.respond_to?(:call) + filter.call(output) + else + output + end + end + end + + # Render this resource without content filters + # @return [String] + Contract Hash, Hash => String + def render_without_filters(opts={}, locs={}) return ::Middleman::FileRenderer.new(@app, file_descriptor[:full_path].to_s).template_data_for_file unless template? md = metadata @@ -151,7 +191,7 @@ module Middleman opts[:layout] = false if !opts.key?(:layout) && !@app.config.extensions_with_layout.include?(ext) renderer = ::Middleman::TemplateRenderer.new(@app, file_descriptor[:full_path].to_s) - renderer.render(locs, opts) + renderer.render(locs, opts).to_str end # A path without the directory index - so foo/index.html becomes @@ -204,9 +244,10 @@ module Middleman end class StringResource < Resource - def initialize(store, path, contents=nil, &block) + Contract IsA['Middleman::Sitemap::Store'], String, Maybe[Or[String, Proc]] => Any + def initialize(store, path, contents) @request_path = path - @contents = block_given? ? block : contents + @contents = contents super(store, path) end @@ -215,7 +256,28 @@ module Middleman end def render(*) - @contents.respond_to?(:call) ? @contents.call : @contents + @contents + end + + def binary? + false + end + end + + class CallbackResource < Resource + Contract IsA['Middleman::Sitemap::Store'], String, Proc => Any + def initialize(store, path, &block) + @request_path = path + @contents = block + super(store, path) + end + + def template? + true + end + + def render(*) + @contents.call end def binary? diff --git a/middleman-core/lib/middleman-core/util/data.rb b/middleman-core/lib/middleman-core/util/data.rb index ce73540a..b0026293 100644 --- a/middleman-core/lib/middleman-core/util/data.rb +++ b/middleman-core/lib/middleman-core/util/data.rb @@ -94,7 +94,7 @@ module Middleman .transpose .map(&::Regexp.method(:union)) - match = / + / \A(?:[^\r\n]*coding:[^\r\n]*\r?\n)? (?#{start_delims})[ ]*\r?\n (?.*?)[ ]*\r?\n? diff --git a/middleman-core/lib/middleman-core/util/files.rb b/middleman-core/lib/middleman-core/util/files.rb index 3d185cea..607b3bd5 100644 --- a/middleman-core/lib/middleman-core/util/files.rb +++ b/middleman-core/lib/middleman-core/util/files.rb @@ -56,10 +56,8 @@ module Middleman Contract String => String def step_through_extensions(path) - while ::Middleman::Util.tilt_class(path) - ext = ::File.extname(path) - break if ext.empty? - + while ext = File.extname(path) + break if ext.empty? || !::Middleman::Util.tilt_class(ext) yield ext if block_given? # Strip templating extensions as long as Tilt knows them diff --git a/middleman-core/lib/middleman-core/util/paths.rb b/middleman-core/lib/middleman-core/util/paths.rb index 26f29442..b6543612 100644 --- a/middleman-core/lib/middleman-core/util/paths.rb +++ b/middleman-core/lib/middleman-core/util/paths.rb @@ -25,13 +25,15 @@ module Middleman def tilt_class(path) ::Tilt[path] end - memoize :tilt_class + # memoize :tilt_class # Normalize a path to not include a leading slash # @param [String] path # @return [String] - Contract String => String + Contract Any => String def normalize_path(path) + return path unless path.is_a?(String) + # The tr call works around a bug in Ruby's Unicode handling ::URI.decode(path).sub(%r{^/}, '').tr('', '') end diff --git a/middleman-core/lib/middleman-core/util/rack.rb b/middleman-core/lib/middleman-core/util/rack.rb index 2459052b..6783ff2d 100644 --- a/middleman-core/lib/middleman-core/util/rack.rb +++ b/middleman-core/lib/middleman-core/util/rack.rb @@ -6,22 +6,9 @@ module Middleman module_function - # Extract the text of a Rack response as a string. - # Useful for extensions implemented as Rack middleware. - # @param response The response from #call - # @return [String] The whole response as a string. - Contract RespondTo[:each] => String - def extract_response_text(response) - # The rack spec states all response bodies must respond to each - result = '' - response.each do |part, _| - result << part - end - result - end - - Contract String, String, ArrayOf[String], IsA['::Middleman::Application'], Proc => String + Contract String, String, SetOf[String], IsA['::Middleman::Application'], Proc => String def rewrite_paths(body, path, exts, app, &_block) + exts = exts.sort_by(&:length).reverse matcher = /([\'\"\(,]\s*|# sourceMappingURL=)([^\s\'\"\)\(>]+(#{::Regexp.union(exts)}))/ url_fn_prefix = 'url(' diff --git a/middleman-core/lib/middleman-core/version.rb b/middleman-core/lib/middleman-core/version.rb index 37f706a4..4fe534de 100644 --- a/middleman-core/lib/middleman-core/version.rb +++ b/middleman-core/lib/middleman-core/version.rb @@ -1,5 +1,5 @@ module Middleman # Current Version # @return [String] - VERSION = '4.1.7'.freeze unless const_defined?(:VERSION) + VERSION = '4.2.0'.freeze unless const_defined?(:VERSION) end