Overhaul content-type handling, making it configurable via page/proxy commands as well as frontmatter with the 'content_type' parameter.
Now, users can set content type explicitly for their files in a number of ways, or rely on automatic file-extension content types. Proxied files default to automatic file-extension content types, but then fall back to the content type of the resource they proxy to. There is also a bug fixed around correctly setting content type inside send_file. Fixes #821.
This commit is contained in:
parent
397ccd2c1e
commit
7a4aa109a6
15 changed files with 118 additions and 53 deletions
43
middleman-core/features/content_type.feature
Normal file
43
middleman-core/features/content_type.feature
Normal file
|
@ -0,0 +1,43 @@
|
|||
Feature: Setting the right content type for files
|
||||
|
||||
Scenario: The right content type gets automatically determined
|
||||
Given the Server is running at "content-type-app"
|
||||
When I go to "/index.html"
|
||||
Then the content type should be "text/html"
|
||||
When I go to "/images/blank.gif"
|
||||
Then the content type should be "image/gif"
|
||||
When I go to "/javascripts/app.js"
|
||||
Then the content type should be "application/javascript"
|
||||
When I go to "/stylesheets/site.css"
|
||||
Then the content type should be "text/css"
|
||||
When I go to "/README"
|
||||
Then the content type should be "text/plain"
|
||||
|
||||
Scenario: Content type can be set explicitly via page or proxy or frontmatter
|
||||
Given a fixture app "content-type-app"
|
||||
And a file named "config.rb" with:
|
||||
"""
|
||||
page "README", :content_type => 'text/awesome'
|
||||
proxy "bar", "index.html", :content_type => 'text/custom'
|
||||
proxy "foo", "README" # auto-delegate to target content type
|
||||
"""
|
||||
And the Server is running at "content-type-app"
|
||||
When I go to "/README"
|
||||
Then the content type should be "text/awesome"
|
||||
When I go to "/bar"
|
||||
Then the content type should be "text/custom"
|
||||
When I go to "/foo"
|
||||
Then the content type should be "text/awesome"
|
||||
When I go to "/override.html"
|
||||
Then the content type should be "text/neato"
|
||||
|
||||
Scenario: Content types can be overridden with mime_type
|
||||
Given a fixture app "content-type-app"
|
||||
And a file named "config.rb" with:
|
||||
"""
|
||||
mime_type('.js', 'application/x-javascript')
|
||||
"""
|
||||
And the Server is running at "content-type-app"
|
||||
When I go to "/javascripts/app.js"
|
||||
Then the content type should be "application/x-javascript"
|
||||
|
1
middleman-core/fixtures/content-type-app/config.rb
Normal file
1
middleman-core/fixtures/content-type-app/config.rb
Normal file
|
@ -0,0 +1 @@
|
|||
|
|
@ -0,0 +1 @@
|
|||
# I'm an htaccess file!
|
1
middleman-core/fixtures/content-type-app/source/README
Normal file
1
middleman-core/fixtures/content-type-app/source/README
Normal file
|
@ -0,0 +1 @@
|
|||
I have no file extension.
|
BIN
middleman-core/fixtures/content-type-app/source/images/blank.gif
Executable file
BIN
middleman-core/fixtures/content-type-app/source/images/blank.gif
Executable file
Binary file not shown.
After Width: | Height: | Size: 43 B |
|
@ -0,0 +1 @@
|
|||
I'm an HTML file!
|
|
@ -0,0 +1 @@
|
|||
// This is JavaScript
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
content_type: 'text/neato'
|
||||
---
|
||||
|
||||
I'm awesome!
|
|
@ -0,0 +1 @@
|
|||
/* This is CSS */
|
|
@ -185,6 +185,15 @@ module Middleman::CoreExtensions
|
|||
def data
|
||||
::Middleman::Util.recursively_enhance(raw_data).freeze
|
||||
end
|
||||
|
||||
# Override Resource#content_type to take into account frontmatter
|
||||
def content_type
|
||||
# Allow setting content type in frontmatter too
|
||||
fm_type = data[:content_type]
|
||||
return fm_type if fm_type
|
||||
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
|
|
|
@ -246,12 +246,11 @@ module Middleman
|
|||
return not_found(res, request_path) unless resource && !resource.ignored?
|
||||
|
||||
# If this path is a binary file, send it immediately
|
||||
return send_file(resource.source_file, env, res) if resource.binary?
|
||||
return send_file(resource, env) if resource.binary?
|
||||
|
||||
current_path = resource.destination_path
|
||||
|
||||
# Set a HTTP content type based on the request's extensions
|
||||
content_type(res, mime_type(resource.ext))
|
||||
res['Content-Type'] = resource.content_type || 'text/plain'
|
||||
|
||||
begin
|
||||
# Write out the contents of the page
|
||||
|
@ -278,11 +277,8 @@ module Middleman
|
|||
# @param [Symbol] type File extension
|
||||
# @param [String] value Mime type
|
||||
# @return [void]
|
||||
def mime_type(type, value=nil)
|
||||
return type if type.nil? || type.to_s.include?('/')
|
||||
return ::Rack::Mime.mime_type('.txt') if type.empty?
|
||||
def mime_type(type, value)
|
||||
type = ".#{type}" unless type.to_s[0] == ?.
|
||||
return ::Rack::Mime.mime_type(type, nil) unless value
|
||||
::Rack::Mime::MIME_TYPES[type] = value
|
||||
end
|
||||
|
||||
|
@ -296,40 +292,14 @@ module Middleman
|
|||
# Immediately send static file
|
||||
#
|
||||
# @param [String] path File to send
|
||||
def send_file(path, env, res)
|
||||
extension = File.extname(path)
|
||||
matched_mime = mime_type(extension)
|
||||
matched_mime = "application/octet-stream" if matched_mime.nil?
|
||||
content_type res, matched_mime
|
||||
|
||||
def send_file(resource, env)
|
||||
file = ::Rack::File.new nil
|
||||
file.path = path
|
||||
file.path = resource.source_file
|
||||
response = file.serving(env)
|
||||
response[1]['Content-Encoding'] = 'gzip' if %w(.svgz).include?(extension)
|
||||
response[1]['Content-Encoding'] = 'gzip' if %w(.svgz .gz).include?(resource.ext)
|
||||
response[1]['Content-Type'] = resource.content_type || "application/octet-stream"
|
||||
halt response
|
||||
end
|
||||
|
||||
# Set the content type for the current request
|
||||
#
|
||||
# @param [String] type Content type
|
||||
# @param [Hash] params
|
||||
# @return [void]
|
||||
def content_type(res, type, params={})
|
||||
return unless type
|
||||
default = params.delete :default
|
||||
mime_type = mime_type(type) || default
|
||||
throw "Unknown media type: %p" % type if mime_type.nil?
|
||||
mime_type = mime_type.dup
|
||||
unless params.include? :charset
|
||||
params[:charset] = params.delete('charset') || "utf-8"
|
||||
end
|
||||
params.delete :charset if mime_type.include? 'charset'
|
||||
unless params.empty?
|
||||
mime_type << (mime_type.include?(';') ? ', ' : ';')
|
||||
mime_type << params.map { |kv| kv.join('=') }.join(', ')
|
||||
end
|
||||
res['Content-Type'] = mime_type
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
require 'rack'
|
||||
|
||||
module Middleman::Sitemap::Extensions
|
||||
# Content type is implemented as a module so it can be overridden by other sitemap extensions
|
||||
module ContentType
|
||||
# The preferred MIME content type for this resource
|
||||
def content_type
|
||||
# Allow explcitly setting content type from page/proxy options
|
||||
meta_type = metadata[:options][:content_type]
|
||||
return meta_type if meta_type
|
||||
|
||||
# Look up mime type based on extension
|
||||
::Rack::Mime.mime_type(ext, nil)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -42,29 +42,39 @@ module Middleman
|
|||
@proxied_to
|
||||
end
|
||||
|
||||
# Whether this page has a template file
|
||||
# @return [Boolean]
|
||||
def template?
|
||||
# The resource for the page this page is proxied to. Throws an exception
|
||||
# if there is no resource.
|
||||
# @return [Sitemap::Resource]
|
||||
def proxied_to_resource
|
||||
proxy_resource = store.find_resource_by_path(proxied_to)
|
||||
|
||||
unless proxy_resource
|
||||
raise "Path #{path} proxies to unknown file #{proxied_to}:#{store.resources.map(&:path)}"
|
||||
end
|
||||
|
||||
if proxy_resource.proxy?
|
||||
raise "You can't proxy #{path} to #{proxied_to} which is itself a proxy."
|
||||
end
|
||||
|
||||
proxy_resource
|
||||
end
|
||||
|
||||
def get_source_file
|
||||
if proxy?
|
||||
store.find_resource_by_path(proxied_to).template?
|
||||
proxied_to_resource.source_file
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def get_source_file
|
||||
def content_type
|
||||
mime_type = super
|
||||
return mime_type if mime_type
|
||||
|
||||
if proxy?
|
||||
proxy_resource = store.find_resource_by_path(proxied_to)
|
||||
|
||||
unless proxy_resource
|
||||
raise "Path #{path} proxies to unknown file #{proxied_to}:#{store.resources.map(&:path)}"
|
||||
end
|
||||
|
||||
if proxy_resource.proxy?
|
||||
raise "You can't proxy #{path} to #{proxied_to} which is itself a proxy."
|
||||
end
|
||||
|
||||
proxy_resource.source_file
|
||||
proxied_to_resource.content_type
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
require "middleman-core/sitemap/extensions/traversal"
|
||||
require "middleman-core/sitemap/extensions/content_type"
|
||||
|
||||
module Middleman
|
||||
|
||||
|
@ -8,6 +9,7 @@ module Middleman
|
|||
# Sitemap Resource class
|
||||
class Resource
|
||||
include Middleman::Sitemap::Extensions::Traversal
|
||||
include Middleman::Sitemap::Extensions::ContentType
|
||||
|
||||
# @return [Middleman::Application]
|
||||
attr_reader :app
|
||||
|
|
|
@ -71,6 +71,10 @@ Then /^going to "([^\"]*)" should not raise an exception$/ do |url|
|
|||
lambda { @browser.get(URI.escape(url)) }.should_not raise_exception
|
||||
end
|
||||
|
||||
Then /^the content type should be "([^\"]*)"$/ do |expected|
|
||||
@browser.last_response.content_type.should start_with(expected)
|
||||
end
|
||||
|
||||
Then /^I should see "([^\"]*)"$/ do |expected|
|
||||
@browser.last_response.body.should include(expected)
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue