From 1ee89ac6bf843898ff9c826c3cbd7b1306642d77 Mon Sep 17 00:00:00 2001 From: Ben Hollis Date: Thu, 11 Oct 2012 21:19:15 -0700 Subject: [PATCH] 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. --- middleman-core/features/proxy_pages.feature | 94 +++++++++++++++++++ .../fixtures/proxy-pages-app/config.rb | 21 +++++ .../fixtures/proxy-pages-app/source/real.html | 1 + .../source/real/index-ivars.html.erb | 6 ++ .../source/real/index.html.erb | 5 + .../source/should_be_ignored3.html | 1 + .../source/should_be_ignored6.html | 1 + .../source/should_be_ignored7.html | 1 + .../source/should_be_ignored8.html | 1 + .../middleman-core/core_extensions/routing.rb | 24 ++--- .../sitemap/extensions/ignores.rb | 2 - .../sitemap/extensions/proxies.rb | 65 +++++++++++-- .../lib/middleman-core/sitemap/store.rb | 18 +--- 13 files changed, 197 insertions(+), 43 deletions(-) create mode 100644 middleman-core/features/proxy_pages.feature create mode 100644 middleman-core/fixtures/proxy-pages-app/config.rb create mode 100644 middleman-core/fixtures/proxy-pages-app/source/real.html create mode 100644 middleman-core/fixtures/proxy-pages-app/source/real/index-ivars.html.erb create mode 100644 middleman-core/fixtures/proxy-pages-app/source/real/index.html.erb create mode 100644 middleman-core/fixtures/proxy-pages-app/source/should_be_ignored3.html create mode 100644 middleman-core/fixtures/proxy-pages-app/source/should_be_ignored6.html create mode 100644 middleman-core/fixtures/proxy-pages-app/source/should_be_ignored7.html create mode 100644 middleman-core/fixtures/proxy-pages-app/source/should_be_ignored8.html diff --git a/middleman-core/features/proxy_pages.feature b/middleman-core/features/proxy_pages.feature new file mode 100644 index 00000000..522f1eb8 --- /dev/null +++ b/middleman-core/features/proxy_pages.feature @@ -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" \ No newline at end of file diff --git a/middleman-core/fixtures/proxy-pages-app/config.rb b/middleman-core/fixtures/proxy-pages-app/config.rb new file mode 100644 index 00000000..0cd260cf --- /dev/null +++ b/middleman-core/fixtures/proxy-pages-app/config.rb @@ -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 diff --git a/middleman-core/fixtures/proxy-pages-app/source/real.html b/middleman-core/fixtures/proxy-pages-app/source/real.html new file mode 100644 index 00000000..cb312952 --- /dev/null +++ b/middleman-core/fixtures/proxy-pages-app/source/real.html @@ -0,0 +1 @@ +I am real \ No newline at end of file diff --git a/middleman-core/fixtures/proxy-pages-app/source/real/index-ivars.html.erb b/middleman-core/fixtures/proxy-pages-app/source/real/index-ivars.html.erb new file mode 100644 index 00000000..07563e7d --- /dev/null +++ b/middleman-core/fixtures/proxy-pages-app/source/real/index-ivars.html.erb @@ -0,0 +1,6 @@ +--- +layout: false +--- + +I am real: <%= @num %> + diff --git a/middleman-core/fixtures/proxy-pages-app/source/real/index.html.erb b/middleman-core/fixtures/proxy-pages-app/source/real/index.html.erb new file mode 100644 index 00000000..db4a78b4 --- /dev/null +++ b/middleman-core/fixtures/proxy-pages-app/source/real/index.html.erb @@ -0,0 +1,5 @@ +--- +layout: false +--- + +I am real: <%= num %> diff --git a/middleman-core/fixtures/proxy-pages-app/source/should_be_ignored3.html b/middleman-core/fixtures/proxy-pages-app/source/should_be_ignored3.html new file mode 100644 index 00000000..98007c81 --- /dev/null +++ b/middleman-core/fixtures/proxy-pages-app/source/should_be_ignored3.html @@ -0,0 +1 @@ +

Ignore me! 3

\ No newline at end of file diff --git a/middleman-core/fixtures/proxy-pages-app/source/should_be_ignored6.html b/middleman-core/fixtures/proxy-pages-app/source/should_be_ignored6.html new file mode 100644 index 00000000..234e60b7 --- /dev/null +++ b/middleman-core/fixtures/proxy-pages-app/source/should_be_ignored6.html @@ -0,0 +1 @@ +

Ignore me! 6

\ No newline at end of file diff --git a/middleman-core/fixtures/proxy-pages-app/source/should_be_ignored7.html b/middleman-core/fixtures/proxy-pages-app/source/should_be_ignored7.html new file mode 100644 index 00000000..4179fb19 --- /dev/null +++ b/middleman-core/fixtures/proxy-pages-app/source/should_be_ignored7.html @@ -0,0 +1 @@ +

Ignore me! 7

\ No newline at end of file diff --git a/middleman-core/fixtures/proxy-pages-app/source/should_be_ignored8.html b/middleman-core/fixtures/proxy-pages-app/source/should_be_ignored8.html new file mode 100644 index 00000000..2c2c24ab --- /dev/null +++ b/middleman-core/fixtures/proxy-pages-app/source/should_be_ignored8.html @@ -0,0 +1 @@ +

Ignore me! 8

\ No newline at end of file diff --git a/middleman-core/lib/middleman-core/core_extensions/routing.rb b/middleman-core/lib/middleman-core/core_extensions/routing.rb index db7c6e61..53f3c3ed 100644 --- a/middleman-core/lib/middleman-core/core_extensions/routing.rb +++ b/middleman-core/lib/middleman-core/core_extensions/routing.rb @@ -45,9 +45,7 @@ module Middleman # @param [Hash] opts # @return [void] def page(url, opts={}, &block) - - blocks = [] - blocks << block if block_given? + blocks = Array(block) # Default layout opts[:layout] = layout if opts[:layout].nil? @@ -70,20 +68,12 @@ module Middleman end # Setup proxy - if opts.has_key?(:proxy) - proxy(url, opts[:proxy]) - - if opts.has_key?(:ignore) && opts[:ignore] - ignore(opts[:proxy]) - opts.delete(:ignore) - end - - opts.delete(:proxy) - else - if opts.has_key?(:ignore) && opts[:ignore] - ignore(url) - opts.delete(:ignore) - end + if target = opts.delete(:proxy) + # TODO: deprecate proxy through page? + proxy(url, target, opts, &block) and return + elsif opts.delete(:ignore) + # TODO: deprecate ignore through page? + ignore(url) end # Setup a metadata matcher for rendering those options diff --git a/middleman-core/lib/middleman-core/sitemap/extensions/ignores.rb b/middleman-core/lib/middleman-core/sitemap/extensions/ignores.rb index c12be353..9c2ea2db 100644 --- a/middleman-core/lib/middleman-core/sitemap/extensions/ignores.rb +++ b/middleman-core/lib/middleman-core/sitemap/extensions/ignores.rb @@ -60,8 +60,6 @@ module Middleman # @param [String, Regexp] path Path glob expression, or path regex # @return [void] def ignore(path=nil, &block) - original_callback_size = @ignored_callbacks.size - if path.is_a? Regexp @ignored_callbacks << Proc.new {|p| p =~ path } elsif path.is_a? String diff --git a/middleman-core/lib/middleman-core/sitemap/extensions/proxies.rb b/middleman-core/lib/middleman-core/sitemap/extensions/proxies.rb index 0d37054a..61091a46 100644 --- a/middleman-core/lib/middleman-core/sitemap/extensions/proxies.rb +++ b/middleman-core/lib/middleman-core/sitemap/extensions/proxies.rb @@ -74,40 +74,87 @@ module Middleman @_proxy_manager ||= ProxyManager.new(self) end - def proxy(*args) - proxy_manager.proxy(*args) + def proxy(*args, &block) + proxy_manager.proxy(*args, &block) end end + # Manages the list of proxy configurations and manipulates the sitemap + # to include new resources based on those configurations class ProxyManager def initialize(app) @app = app - - @proxy_paths = {} + @proxy_configs = Set.new end # Setup a proxy from a path to a target # @param [String] path # @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] - def proxy(path, target) - @proxy_paths[::Middleman::Util.normalize_path(path)] = ::Middleman::Util.normalize_path(target) + def proxy(path, target, opts={}, &block) + 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) end # Update the main sitemap resource list # @return [void] def manipulate_resource_list(resources) - resources + @proxy_paths.map do |key, value| + resources + @proxy_configs.map do |config| p = ::Middleman::Sitemap::Resource.new( @app.sitemap, - key + config.path ) - p.proxy_to(value) + p.proxy_to(config.target) + p.add_metadata(config.metadata) p 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 diff --git a/middleman-core/lib/middleman-core/sitemap/store.rb b/middleman-core/lib/middleman-core/sitemap/store.rb index 0b4a1f71..6eb0d300 100644 --- a/middleman-core/lib/middleman-core/sitemap/store.rb +++ b/middleman-core/lib/middleman-core/sitemap/store.rb @@ -115,19 +115,10 @@ module Middleman # @param [Symbol] origin an indicator of where this metadata came from - only one # block per [matcher, origin] pair may exist. # @return [Array>] - def provides_metadata_for_path(matcher=nil, origin=nil, &block) + def provides_metadata_for_path(matcher=nil, &block) @_provides_metadata_for_path ||= [] if block_given? - if origin - 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 - + @_provides_metadata_for_path << [block, matcher] @_cached_metadata = {} end @_provides_metadata_for_path @@ -151,10 +142,7 @@ module Middleman metadata = callback.call(request_path) - if metadata.has_key?(:blocks) - result[:blocks] << metadata[:blocks] - metadata.delete(:blocks) - end + result[:blocks] += Array(metadata.delete(:blocks)) result.deep_merge(metadata) end