implement redirect support
This commit is contained in:
parent
912b33929f
commit
d86dffa7c6
8 changed files with 271 additions and 127 deletions
|
@ -1,3 +1,8 @@
|
|||
3.1.2
|
||||
===
|
||||
|
||||
* Added `redirect` command for generating meta refreshes
|
||||
|
||||
3.1.1
|
||||
===
|
||||
|
||||
|
|
60
middleman-core/features/redirects.feature
Normal file
60
middleman-core/features/redirects.feature
Normal file
|
@ -0,0 +1,60 @@
|
|||
Feature: Meta redirects
|
||||
|
||||
Scenario: Redirect to unknown file
|
||||
Given a fixture app "large-build-app"
|
||||
And a file named "config.rb" with:
|
||||
"""
|
||||
redirect "hello.html", :to => "world.html"
|
||||
"""
|
||||
And the Server is running at "large-build-app"
|
||||
When I go to "/hello.html"
|
||||
Then I should see '<meta http-equiv=refresh content="0; url=world.html"'
|
||||
|
||||
Scenario: Redirect to external site
|
||||
Given a fixture app "large-build-app"
|
||||
And a file named "config.rb" with:
|
||||
"""
|
||||
redirect "hello.html", :to => "http://example.com"
|
||||
"""
|
||||
And the Server is running at "large-build-app"
|
||||
When I go to "/hello.html"
|
||||
Then I should see '<meta http-equiv=refresh content="0; url=http://example.com"'
|
||||
|
||||
Scenario: Redirect to a resource
|
||||
Given a fixture app "large-build-app"
|
||||
And a file named "config.rb" with:
|
||||
"""
|
||||
ready do
|
||||
r = sitemap.find_resource_by_path("static.html")
|
||||
redirect "hello.html", :to => r
|
||||
end
|
||||
"""
|
||||
And the Server is running at "large-build-app"
|
||||
When I go to "/hello.html"
|
||||
Then I should see '<meta http-equiv=refresh content="0; url=/static.html"'
|
||||
|
||||
Scenario: Redirect to a path with directory index
|
||||
Given a fixture app "large-build-app"
|
||||
And a file named "config.rb" with:
|
||||
"""
|
||||
activate :directory_indexes
|
||||
redirect "hello.html", :to => "link_test.html"
|
||||
redirect "hello2.html", :to => "services/index.html"
|
||||
"""
|
||||
And the Server is running at "large-build-app"
|
||||
When I go to "/hello/index.html"
|
||||
Then I should see '<meta http-equiv=refresh content="0; url=/link_test/"'
|
||||
When I go to "/hello2/index.html"
|
||||
Then I should see '<meta http-equiv=refresh content="0; url=/services/"'
|
||||
|
||||
Scenario: Redirect with custom html
|
||||
Given a fixture app "large-build-app"
|
||||
And a file named "config.rb" with:
|
||||
"""
|
||||
redirect "hello.html", :to => "world.html" do |from, to|
|
||||
"#{from} to #{to}"
|
||||
end
|
||||
"""
|
||||
And the Server is running at "large-build-app"
|
||||
When I go to "/hello.html"
|
||||
Then I should see 'hello.html to world.html'
|
|
@ -2,6 +2,7 @@ require "middleman-core/sitemap/store"
|
|||
require "middleman-core/sitemap/resource"
|
||||
|
||||
require "middleman-core/sitemap/extensions/on_disk"
|
||||
require "middleman-core/sitemap/extensions/redirects"
|
||||
require "middleman-core/sitemap/extensions/request_endpoints"
|
||||
require "middleman-core/sitemap/extensions/proxies"
|
||||
require "middleman-core/sitemap/extensions/ignores"
|
||||
|
@ -20,6 +21,7 @@ module Middleman
|
|||
app.register Middleman::Sitemap::Extensions::RequestEndpoints
|
||||
app.register Middleman::Sitemap::Extensions::Proxies
|
||||
app.register Middleman::Sitemap::Extensions::Ignores
|
||||
app.register Middleman::Sitemap::Extensions::Redirects
|
||||
|
||||
# Set to automatically convert some characters into a directory
|
||||
app.config.define_setting :automatic_directory_matcher, nil, 'Set to automatically convert some characters into a directory'
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
module Middleman
|
||||
|
||||
module Sitemap
|
||||
|
||||
module Extensions
|
||||
|
||||
module Redirects
|
||||
|
||||
# Setup extension
|
||||
class << self
|
||||
|
||||
# Once registered
|
||||
def registered(app)
|
||||
# Include methods
|
||||
app.send :include, InstanceMethods
|
||||
end
|
||||
|
||||
alias :included :registered
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
def redirect_manager
|
||||
@_redirect_manager ||= RedirectManager.new(self)
|
||||
end
|
||||
|
||||
def redirect(*args, &block)
|
||||
redirect_manager.create_redirect(*args, &block)
|
||||
end
|
||||
end
|
||||
|
||||
# Manages the list of proxy configurations and manipulates the sitemap
|
||||
# to include new resources based on those configurations
|
||||
class RedirectManager
|
||||
def initialize(app)
|
||||
@app = app
|
||||
@redirects = {}
|
||||
end
|
||||
|
||||
# Setup a redirect from a path to a target
|
||||
# @param [String] path
|
||||
# @param [Hash] The :to value gives a target path
|
||||
# @return [void]
|
||||
def create_redirect(path, opts={}, &block)
|
||||
if block_given?
|
||||
opts[:template] = block
|
||||
end
|
||||
|
||||
@redirects[path] = opts
|
||||
|
||||
@app.sitemap.rebuild_resource_list!(:added_redirect)
|
||||
end
|
||||
|
||||
# Update the main sitemap resource list
|
||||
# @return [void]
|
||||
def manipulate_resource_list(resources)
|
||||
resources + @redirects.map do |path, opts|
|
||||
r = RedirectResource.new(
|
||||
@app.sitemap,
|
||||
path,
|
||||
opts[:to]
|
||||
)
|
||||
r.output = opts[:template] if opts[:template]
|
||||
r
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class RedirectResource < ::Middleman::Sitemap::Resource
|
||||
attr_accessor :output
|
||||
|
||||
def initialize(store, path, target)
|
||||
@request_path = target
|
||||
|
||||
super(store, path)
|
||||
end
|
||||
|
||||
def template?
|
||||
true
|
||||
end
|
||||
|
||||
def render(*args, &block)
|
||||
url = ::Middleman::Util.url_for(store.app, @request_path, :relative => false, :find_resource => true)
|
||||
|
||||
if output
|
||||
output.call(path, url)
|
||||
else
|
||||
<<-END
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv=refresh content="0; url=#{url}" />
|
||||
<meta name="robots" content="noindex,follow" />
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
END
|
||||
end
|
||||
end
|
||||
|
||||
# def request_path
|
||||
# @request_path
|
||||
# end
|
||||
|
||||
def binary?
|
||||
false
|
||||
end
|
||||
|
||||
def raw_data
|
||||
{}
|
||||
end
|
||||
|
||||
def ignored?
|
||||
false
|
||||
end
|
||||
|
||||
def metadata
|
||||
@local_metadata.dup
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -11,8 +11,6 @@ module Middleman
|
|||
|
||||
# Once registered
|
||||
def registered(app)
|
||||
# ::Middleman::Sitemap::Resource.send :include, ResourceInstanceMethods
|
||||
|
||||
# Include methods
|
||||
app.send :include, InstanceMethods
|
||||
end
|
||||
|
@ -20,65 +18,6 @@ module Middleman
|
|||
alias :included :registered
|
||||
end
|
||||
|
||||
# module ResourceInstanceMethods
|
||||
# # Whether this page is a proxy
|
||||
# # @return [Boolean]
|
||||
# def proxy?
|
||||
# !!@proxied_to
|
||||
# end
|
||||
|
||||
# # Set this page to proxy to a target path
|
||||
# # @param [String] target
|
||||
# # @return [void]
|
||||
# def proxy_to(target)
|
||||
# target = ::Middleman::Util.normalize_path(target)
|
||||
# raise "You can't proxy #{path} to itself!" if target == path
|
||||
# @proxied_to = target
|
||||
# end
|
||||
|
||||
# # The path of the page this page is proxied to, or nil if it's not proxied.
|
||||
# # @return [String]
|
||||
# def proxied_to
|
||||
# @proxied_to
|
||||
# end
|
||||
|
||||
# # 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?
|
||||
# proxied_to_resource.source_file
|
||||
# else
|
||||
# super
|
||||
# end
|
||||
# end
|
||||
|
||||
# def content_type
|
||||
# mime_type = super
|
||||
# return mime_type if mime_type
|
||||
|
||||
# if proxy?
|
||||
# proxied_to_resource.content_type
|
||||
# else
|
||||
# nil
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
|
||||
module InstanceMethods
|
||||
def endpoint_manager
|
||||
@_endpoint_manager ||= EndpointManager.new(self)
|
||||
|
|
|
@ -41,6 +41,9 @@ module Middleman
|
|||
|
||||
# Proxies
|
||||
register_resource_list_manipulator(:proxies, @app.proxy_manager)
|
||||
|
||||
# Redirects
|
||||
register_resource_list_manipulator(:redirects, @app.redirect_manager)
|
||||
end
|
||||
|
||||
# Register a klass which can manipulate the main site map list. Best to register
|
||||
|
|
|
@ -146,5 +146,81 @@ module Middleman
|
|||
end
|
||||
end.flatten.compact
|
||||
end
|
||||
|
||||
# Given a source path (referenced either absolutely or relatively)
|
||||
# or a Resource, this will produce the nice URL configured for that
|
||||
# path, respecting :relative_links, directory indexes, etc.
|
||||
def self.url_for(app, path_or_resource, options={})
|
||||
# Handle Resources and other things which define their own url method
|
||||
url = path_or_resource.respond_to?(:url) ? path_or_resource.url : path_or_resource
|
||||
|
||||
begin
|
||||
uri = URI(url)
|
||||
rescue URI::InvalidURIError
|
||||
# Nothing we can do with it, it's not really a URI
|
||||
return url
|
||||
end
|
||||
|
||||
relative = options.delete(:relative)
|
||||
raise "Can't use the relative option with an external URL" if relative && uri.host
|
||||
|
||||
# Allow people to turn on relative paths for all links with
|
||||
# set :relative_links, true
|
||||
# but still override on a case by case basis with the :relative parameter.
|
||||
effective_relative = relative || false
|
||||
effective_relative = true if relative.nil? && app.config[:relative_links]
|
||||
|
||||
# Try to find a sitemap resource corresponding to the desired path
|
||||
this_resource = app.current_resource # store in a local var to save work
|
||||
|
||||
if path_or_resource.is_a?(::Middleman::Sitemap::Resource)
|
||||
resource = path_or_resource
|
||||
resource_url = url
|
||||
elsif this_resource && uri.path
|
||||
# Handle relative urls
|
||||
url_path = Pathname(uri.path)
|
||||
current_source_dir = Pathname('/' + this_resource.path).dirname
|
||||
url_path = current_source_dir.join(url_path) if url_path.relative?
|
||||
resource = app.sitemap.find_resource_by_path(url_path.to_s)
|
||||
resource_url = resource.url if resource
|
||||
elsif options[:find_resource] && uri.path
|
||||
resource = app.sitemap.find_resource_by_path(uri.path)
|
||||
resource_url = resource.url if resource
|
||||
end
|
||||
|
||||
if resource
|
||||
# Switch to the relative path between this_resource and the given resource
|
||||
# if we've been asked to.
|
||||
if effective_relative
|
||||
# Output urls relative to the destination path, not the source path
|
||||
current_dir = Pathname('/' + this_resource.destination_path).dirname
|
||||
relative_path = Pathname(resource_url).relative_path_from(current_dir).to_s
|
||||
|
||||
# Put back the trailing slash to avoid unnecessary Apache redirects
|
||||
if resource_url.end_with?('/') && !relative_path.end_with?('/')
|
||||
relative_path << '/'
|
||||
end
|
||||
|
||||
uri.path = relative_path
|
||||
else
|
||||
uri.path = resource_url
|
||||
end
|
||||
else
|
||||
# If they explicitly asked for relative links but we can't find a resource...
|
||||
raise "No resource exists at #{url}" if relative
|
||||
end
|
||||
|
||||
# Support a :query option that can be a string or hash
|
||||
if query = options.delete(:query)
|
||||
uri.query = query.respond_to?(:to_param) ? query.to_param : query.to_s
|
||||
end
|
||||
|
||||
# Support a :fragment or :anchor option just like Padrino
|
||||
fragment = options.delete(:anchor) || options.delete(:fragment)
|
||||
uri.fragment = fragment.to_s if fragment
|
||||
|
||||
# Finally make the URL back into a string
|
||||
uri.to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -189,72 +189,7 @@ class Middleman::CoreExtensions::DefaultHelpers < ::Middleman::Extension
|
|||
# or a Resource, this will produce the nice URL configured for that
|
||||
# path, respecting :relative_links, directory indexes, etc.
|
||||
def url_for(path_or_resource, options={})
|
||||
# Handle Resources and other things which define their own url method
|
||||
url = path_or_resource.respond_to?(:url) ? path_or_resource.url : path_or_resource
|
||||
|
||||
begin
|
||||
uri = URI(url)
|
||||
rescue URI::InvalidURIError
|
||||
# Nothing we can do with it, it's not really a URI
|
||||
return url
|
||||
end
|
||||
|
||||
relative = options.delete(:relative)
|
||||
raise "Can't use the relative option with an external URL" if relative && uri.host
|
||||
|
||||
# Allow people to turn on relative paths for all links with
|
||||
# set :relative_links, true
|
||||
# but still override on a case by case basis with the :relative parameter.
|
||||
effective_relative = relative || false
|
||||
effective_relative = true if relative.nil? && config[:relative_links]
|
||||
|
||||
# Try to find a sitemap resource corresponding to the desired path
|
||||
this_resource = current_resource # store in a local var to save work
|
||||
if path_or_resource.is_a?(::Middleman::Sitemap::Resource)
|
||||
resource = path_or_resource
|
||||
resource_url = url
|
||||
elsif this_resource && uri.path
|
||||
# Handle relative urls
|
||||
url_path = Pathname(uri.path)
|
||||
current_source_dir = Pathname('/' + this_resource.path).dirname
|
||||
url_path = current_source_dir.join(url_path) if url_path.relative?
|
||||
resource = sitemap.find_resource_by_path(url_path.to_s)
|
||||
resource_url = resource.url if resource
|
||||
end
|
||||
|
||||
if resource
|
||||
# Switch to the relative path between this_resource and the given resource
|
||||
# if we've been asked to.
|
||||
if effective_relative
|
||||
# Output urls relative to the destination path, not the source path
|
||||
current_dir = Pathname('/' + this_resource.destination_path).dirname
|
||||
relative_path = Pathname(resource_url).relative_path_from(current_dir).to_s
|
||||
|
||||
# Put back the trailing slash to avoid unnecessary Apache redirects
|
||||
if resource_url.end_with?('/') && !relative_path.end_with?('/')
|
||||
relative_path << '/'
|
||||
end
|
||||
|
||||
uri.path = relative_path
|
||||
else
|
||||
uri.path = resource_url
|
||||
end
|
||||
else
|
||||
# If they explicitly asked for relative links but we can't find a resource...
|
||||
raise "No resource exists at #{url}" if relative
|
||||
end
|
||||
|
||||
# Support a :query option that can be a string or hash
|
||||
if query = options.delete(:query)
|
||||
uri.query = query.respond_to?(:to_param) ? query.to_param : query.to_s
|
||||
end
|
||||
|
||||
# Support a :fragment or :anchor option just like Padrino
|
||||
fragment = options.delete(:anchor) || options.delete(:fragment)
|
||||
uri.fragment = fragment.to_s if fragment
|
||||
|
||||
# Finally make the URL back into a string
|
||||
uri.to_s
|
||||
::Middleman::Util.url_for(self, path_or_resource, options)
|
||||
end
|
||||
|
||||
# Overload the regular link_to to be sitemap-aware - if you
|
||||
|
|
Loading…
Reference in a new issue