Upgrade proxy to be able to take :locals and understand :ignore, and store proxy options and metadata with the proxy list.

This allows users to use proxy directly instead of page to create dynamic pages, and improves performance of dynamic pages for sites that create many proxies. It also allows people to use locals instead of instance variables, which are better for partials and reduce the risk of overwriting Middleman settings.
This commit is contained in:
Ben Hollis 2012-10-11 21:19:15 -07:00
parent 42f9e331da
commit 1ee89ac6bf
13 changed files with 197 additions and 43 deletions

View file

@ -0,0 +1,94 @@
Feature: Proxy Pages (using proxy rather than page)
In order to use a single view to generate multiple output files
Scenario: Checking built folder for content
Given a successfully built app at "proxy-pages-app"
When I cd to "build"
Then the following files should exist:
| fake.html |
| fake2.html |
| fake3.html |
| fake4.html |
| fake/one.html |
| fake/two.html |
| fake2/one.html |
| fake2/two.html |
| fake3/one.html |
| fake3/two.html |
| fake4/one.html |
| fake4/two.html |
| target_ignore.html |
| target_ignore2.html |
| target_ignore3.html |
| target_ignore4.html |
| .html |
Then the following files should not exist:
| should_be_ignored6.html |
| should_be_ignored7.html |
| should_be_ignored8.html |
Scenario: Preview basic proxy
Given the Server is running at "proxy-pages-app"
When I go to "/fake.html"
Then I should see "I am real"
When I go to "/fake2.html"
Then I should see "I am real"
When I go to "/fake3.html"
Then I should see "I am real"
When I go to "/fake4.html"
Then I should see "I am real"
Scenario: Preview proxy with variable one
Given the Server is running at "proxy-pages-app"
When I go to "/fake/one.html"
Then I should see "I am real: one"
When I go to "/fake2/one.html"
Then I should see "I am real: one"
When I go to "/fake3/one.html"
Then I should see "I am real: one"
When I go to "/fake4/one.html"
Then I should see "I am real: one"
Scenario: Preview proxy with variable two
Given the Server is running at "proxy-pages-app"
When I go to "/fake/two.html"
Then I should see "I am real: two"
When I go to "/fake2/two.html"
Then I should see "I am real: two"
When I go to "/fake3/two.html"
Then I should see "I am real: two"
When I go to "/fake4/two.html"
Then I should see "I am real: two"
Scenario: Build proxy with variable one
Given a successfully built app at "proxy-pages-app"
When I cd to "build"
Then the file "fake/one.html" should contain "I am real: one"
Then the file "fake2/one.html" should contain "I am real: one"
Then the file "fake3/one.html" should contain "I am real: one"
Scenario: Target ignore
Given the Server is running at "proxy-pages-app"
When I go to "/target_ignore.html"
Then I should see "Ignore me! 3"
When I go to "/target_ignore2.html"
Then I should see "Ignore me! 6"
When I go to "/target_ignore3.html"
Then I should see "Ignore me! 7"
When I go to "/target_ignore4.html"
Then I should see "Ignore me! 8"
Scenario: Preview ignored paths
Given the Server is running at "proxy-pages-app"
When I go to "/should_be_ignored6.html"
Then I should see "File Not Found"
When I go to "/should_be_ignored7.html"
Then I should see "File Not Found"
When I go to "/should_be_ignored8.html"
Then I should see "File Not Found"

View file

@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
proxy "/fake.html", "/real.html", :layout => false
proxy "fake2.html", "/real.html", :layout => false
proxy "fake3.html", "real.html", :layout => false
proxy "/fake4.html", "real.html", :layout => false
proxy "/target_ignore.html", "/should_be_ignored3.html", :ignore => true
proxy "target_ignore2.html", "/should_be_ignored6.html", :ignore => true
proxy "target_ignore3.html", "should_be_ignored7.html", :ignore => true
proxy "/target_ignore4.html", "should_be_ignored8.html", :ignore => true
%w(one two).each do |num|
proxy "/fake/#{num}.html", "/real/index.html", :ignore => true, :locals => { :num => num }
proxy "fake2/#{num}.html", "/real/index.html", :ignore => true, :locals => { :num => num }
proxy "fake3/#{num}.html", "real/index.html", :ignore => true, :locals => { :num => num }
proxy "/fake4/#{num}.html", "real/index-ivars.html", :ignore => true do
@num = num
end
end
proxy "明日がある.html", "/real.html", :layout => false

View file

@ -0,0 +1 @@
I am real

View file

@ -0,0 +1,6 @@
---
layout: false
---
I am real: <%= @num %>

View file

@ -0,0 +1,5 @@
---
layout: false
---
I am real: <%= num %>

View file

@ -0,0 +1 @@
<h1>Ignore me! 3</h1>

View file

@ -0,0 +1 @@
<h1>Ignore me! 6</h1>

View file

@ -0,0 +1 @@
<h1>Ignore me! 7</h1>

View file

@ -0,0 +1 @@
<h1>Ignore me! 8</h1>

View file

@ -45,9 +45,7 @@ module Middleman
# @param [Hash] opts # @param [Hash] opts
# @return [void] # @return [void]
def page(url, opts={}, &block) def page(url, opts={}, &block)
blocks = Array(block)
blocks = []
blocks << block if block_given?
# Default layout # Default layout
opts[:layout] = layout if opts[:layout].nil? opts[:layout] = layout if opts[:layout].nil?
@ -70,20 +68,12 @@ module Middleman
end end
# Setup proxy # Setup proxy
if opts.has_key?(:proxy) if target = opts.delete(:proxy)
proxy(url, opts[:proxy]) # TODO: deprecate proxy through page?
proxy(url, target, opts, &block) and return
if opts.has_key?(:ignore) && opts[:ignore] elsif opts.delete(:ignore)
ignore(opts[:proxy]) # TODO: deprecate ignore through page?
opts.delete(:ignore) ignore(url)
end
opts.delete(:proxy)
else
if opts.has_key?(:ignore) && opts[:ignore]
ignore(url)
opts.delete(:ignore)
end
end end
# Setup a metadata matcher for rendering those options # Setup a metadata matcher for rendering those options

View file

@ -60,8 +60,6 @@ module Middleman
# @param [String, Regexp] path Path glob expression, or path regex # @param [String, Regexp] path Path glob expression, or path regex
# @return [void] # @return [void]
def ignore(path=nil, &block) def ignore(path=nil, &block)
original_callback_size = @ignored_callbacks.size
if path.is_a? Regexp if path.is_a? Regexp
@ignored_callbacks << Proc.new {|p| p =~ path } @ignored_callbacks << Proc.new {|p| p =~ path }
elsif path.is_a? String elsif path.is_a? String

View file

@ -74,40 +74,87 @@ module Middleman
@_proxy_manager ||= ProxyManager.new(self) @_proxy_manager ||= ProxyManager.new(self)
end end
def proxy(*args) def proxy(*args, &block)
proxy_manager.proxy(*args) proxy_manager.proxy(*args, &block)
end end
end end
# Manages the list of proxy configurations and manipulates the sitemap
# to include new resources based on those configurations
class ProxyManager class ProxyManager
def initialize(app) def initialize(app)
@app = app @app = app
@proxy_configs = Set.new
@proxy_paths = {}
end end
# Setup a proxy from a path to a target # Setup a proxy from a path to a target
# @param [String] path # @param [String] path
# @param [String] target # @param [String] target
# @param [Hash] opts options to apply to the proxy, including things like
# :locals, :ignore to hide the proxy target, :layout, and :directory_indexes.
# @return [void] # @return [void]
def proxy(path, target) def proxy(path, target, opts={}, &block)
@proxy_paths[::Middleman::Util.normalize_path(path)] = ::Middleman::Util.normalize_path(target) metadata = { :options => {}, :locals => {}, :blocks => [] }
metadata[:blocks] << block if block_given?
metadata[:locals] = opts.delete(:locals) || {}
@app.ignore(target) if opts.delete(:ignore)
metadata[:options] = opts
@proxy_configs << ProxyConfiguration.new(:path => path, :target => target, :metadata => metadata)
@app.sitemap.rebuild_resource_list!(:added_proxy) @app.sitemap.rebuild_resource_list!(:added_proxy)
end end
# Update the main sitemap resource list # Update the main sitemap resource list
# @return [void] # @return [void]
def manipulate_resource_list(resources) def manipulate_resource_list(resources)
resources + @proxy_paths.map do |key, value| resources + @proxy_configs.map do |config|
p = ::Middleman::Sitemap::Resource.new( p = ::Middleman::Sitemap::Resource.new(
@app.sitemap, @app.sitemap,
key config.path
) )
p.proxy_to(value) p.proxy_to(config.target)
p.add_metadata(config.metadata)
p p
end end
end end
end end
# Configuration for a proxy instance
class ProxyConfiguration
# The path that this proxy will appear at in the sitemap
attr_reader :path
def path=(p)
@path = ::Middleman::Util.normalize_path(p)
end
# The existing sitemap path that this will proxy to
attr_reader :target
def target=(t)
@target = ::Middleman::Util.normalize_path(t)
end
# Additional metadata like blocks and locals to apply to the proxy
attr_accessor :metadata
# Create a new proxy configuration from hash options
def initialize(options={})
options.each do |key, value|
send "#{key}=", value
end
end
# Two configurations are equal if they reference the same path
def eql?(other)
other.path == path
end
# Two configurations are equal if they reference the same path
def hash
path.hash
end
end
end end
end end
end end

View file

@ -115,19 +115,10 @@ module Middleman
# @param [Symbol] origin an indicator of where this metadata came from - only one # @param [Symbol] origin an indicator of where this metadata came from - only one
# block per [matcher, origin] pair may exist. # block per [matcher, origin] pair may exist.
# @return [Array<Array<Proc, Regexp>>] # @return [Array<Array<Proc, Regexp>>]
def provides_metadata_for_path(matcher=nil, origin=nil, &block) def provides_metadata_for_path(matcher=nil, &block)
@_provides_metadata_for_path ||= [] @_provides_metadata_for_path ||= []
if block_given? if block_given?
if origin @_provides_metadata_for_path << [block, matcher]
existing_provider = @_provides_metadata_for_path.find {|b,m,o| o == origin && m == matcher}
end
if existing_provider
existing_provider[0] = block
else
@_provides_metadata_for_path << [block, matcher, origin]
end
@_cached_metadata = {} @_cached_metadata = {}
end end
@_provides_metadata_for_path @_provides_metadata_for_path
@ -151,10 +142,7 @@ module Middleman
metadata = callback.call(request_path) metadata = callback.call(request_path)
if metadata.has_key?(:blocks) result[:blocks] += Array(metadata.delete(:blocks))
result[:blocks] << metadata[:blocks]
metadata.delete(:blocks)
end
result.deep_merge(metadata) result.deep_merge(metadata)
end end