diff --git a/CHANGELOG.md b/CHANGELOG.md
index 89a3371f..b7bec4c8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,7 @@
master
===
+* Add `page_id` concept. Using the `id` key in frontmatter, proxy or page will set an ID on a resource which can be referenced by `url_for` and `link_to`.
* Allow looking for `Gemfile` when setting up a project to fail gracefully.
# 4.1.1
diff --git a/middleman-core/features/page-id.feature b/middleman-core/features/page-id.feature
new file mode 100644
index 00000000..63dd6fad
--- /dev/null
+++ b/middleman-core/features/page-id.feature
@@ -0,0 +1,26 @@
+Feature: Page IDs
+
+ Scenario: link_to works with blocks (erb)
+ Given the Server is running at "page-id-app"
+ When I go to "/index.html"
+ Then I should see "I am: index.html"
+ And I should see "URL1: /fm.html"
+ And I should see "URL2: /2.html"
+ And I should see 'URL3: Hi'
+ And I should see 'URL4: Sym'
+
+ When I go to "/fm.html"
+ Then I should see "I am: frontmatter"
+
+ When I go to "/1.html"
+ Then I should see "I am: page1"
+ When I go to "/2.html"
+ Then I should see "I am: page2"
+ When I go to "/3.html"
+ Then I should see "I am: page3"
+
+ When I go to "/overwrites/from-default.html"
+ Then I should see "I am: something-else"
+
+ When I go to "/overwrites/from-frontmatter.html"
+ Then I should see "I am: from_frontmatter"
diff --git a/middleman-core/fixtures/page-id-app/config.rb b/middleman-core/fixtures/page-id-app/config.rb
new file mode 100644
index 00000000..d0475e7a
--- /dev/null
+++ b/middleman-core/fixtures/page-id-app/config.rb
@@ -0,0 +1,5 @@
+%w(1 2 3).each do |n|
+ proxy "/#{n}.html", "/index.html", id: "page#{n}"
+end
+
+page "/overwrites/*", id: :"something-else"
diff --git a/middleman-core/fixtures/page-id-app/source/fm.html.erb b/middleman-core/fixtures/page-id-app/source/fm.html.erb
new file mode 100644
index 00000000..4d6d0772
--- /dev/null
+++ b/middleman-core/fixtures/page-id-app/source/fm.html.erb
@@ -0,0 +1,5 @@
+---
+id: frontmatter
+---
+
+I am: <%= current_resource.page_id %>
diff --git a/middleman-core/fixtures/page-id-app/source/index.html.erb b/middleman-core/fixtures/page-id-app/source/index.html.erb
new file mode 100644
index 00000000..8f2997b6
--- /dev/null
+++ b/middleman-core/fixtures/page-id-app/source/index.html.erb
@@ -0,0 +1,6 @@
+I am: <%= current_resource.page_id %>
+
+URL1: <%= url_for "frontmatter" %>
+URL2: <%= url_for "page2" %>
+URL3: <%= link_to "Hi", "page3" %>
+URL4: <%= link_to "Sym", :"something-else" %>
diff --git a/middleman-core/fixtures/page-id-app/source/overwrites/from-default.html.erb b/middleman-core/fixtures/page-id-app/source/overwrites/from-default.html.erb
new file mode 100644
index 00000000..a27800bd
--- /dev/null
+++ b/middleman-core/fixtures/page-id-app/source/overwrites/from-default.html.erb
@@ -0,0 +1 @@
+I am: <%= current_resource.page_id %>
diff --git a/middleman-core/fixtures/page-id-app/source/overwrites/from-frontmatter.html.erb b/middleman-core/fixtures/page-id-app/source/overwrites/from-frontmatter.html.erb
new file mode 100644
index 00000000..194cf4c1
--- /dev/null
+++ b/middleman-core/fixtures/page-id-app/source/overwrites/from-frontmatter.html.erb
@@ -0,0 +1,5 @@
+---
+id: from_frontmatter
+---
+
+I am: <%= current_resource.page_id %>
diff --git a/middleman-core/lib/middleman-core/core_extensions/routing.rb b/middleman-core/lib/middleman-core/core_extensions/routing.rb
index 089b0486..b10fba53 100644
--- a/middleman-core/lib/middleman-core/core_extensions/routing.rb
+++ b/middleman-core/lib/middleman-core/core_extensions/routing.rb
@@ -52,10 +52,13 @@ module Middleman
def page(path, opts={})
options = opts.dup
+ page_data = options.delete(:data) || {}
+ page_data[:id] = options.delete(:id) if options.key?(:id)
+
# Default layout
metadata = {
locals: options.delete(:locals) || {},
- page: options.delete(:data) || {},
+ page: page_data,
options: options
}
diff --git a/middleman-core/lib/middleman-core/sitemap/extensions/proxies.rb b/middleman-core/lib/middleman-core/sitemap/extensions/proxies.rb
index 33c0f9a5..b11e02eb 100644
--- a/middleman-core/lib/middleman-core/sitemap/extensions/proxies.rb
+++ b/middleman-core/lib/middleman-core/sitemap/extensions/proxies.rb
@@ -36,10 +36,13 @@ module Middleman
md = metadata.dup
should_ignore = md.delete(:ignore)
+ page_data = md.delete(:data) || {}
+ page_data[:id] = md.delete(:id) if md.key?(:id)
+
r = ProxyResource.new(app.sitemap, path, target)
r.add_metadata(
locals: md.delete(:locals) || {},
- page: md.delete(:data) || {},
+ page: page_data || {},
options: md
)
diff --git a/middleman-core/lib/middleman-core/sitemap/resource.rb b/middleman-core/lib/middleman-core/sitemap/resource.rb
index b7b0e063..22213c21 100644
--- a/middleman-core/lib/middleman-core/sitemap/resource.rb
+++ b/middleman-core/lib/middleman-core/sitemap/resource.rb
@@ -85,6 +85,11 @@ module Middleman
file_descriptor && file_descriptor[:full_path].to_s
end
+ Contract Or[Symbol, String]
+ def page_id
+ metadata[:page][:id] || destination_path
+ end
+
# Merge in new metadata specific to this resource.
# @param [Hash] meta A metadata block with keys :options, :locals, :page.
# Options are generally rendering/sitemap options
diff --git a/middleman-core/lib/middleman-core/sitemap/store.rb b/middleman-core/lib/middleman-core/sitemap/store.rb
index 41037309..a7ade5fc 100644
--- a/middleman-core/lib/middleman-core/sitemap/store.rb
+++ b/middleman-core/lib/middleman-core/sitemap/store.rb
@@ -149,6 +149,17 @@ module Middleman
end
end
+ # Find a resource given its page id
+ # @param [String] page_id The page id.
+ # @return [Middleman::Sitemap::Resource]
+ Contract Or[String, Symbol] => Maybe[IsA['Middleman::Sitemap::Resource']]
+ def find_resource_by_page_id(page_id)
+ @lock.synchronize do
+ ensure_resource_list_updated!
+ @_lookup_by_page_id[page_id.to_sym]
+ end
+ end
+
# Get the array of all resources
# @param [Boolean] include_ignored Whether to include ignored resources
# @return [Array]
@@ -220,6 +231,7 @@ module Middleman
@resources.each do |resource|
@_lookup_by_path[resource.path] = resource
@_lookup_by_destination_path[resource.destination_path] = resource
+ @_lookup_by_page_id[resource.page_id.to_sym] = resource
end
invalidate_resources_not_ignored_cache!
@@ -239,6 +251,7 @@ module Middleman
@lock.synchronize do
@_lookup_by_path = {}
@_lookup_by_destination_path = {}
+ @_lookup_by_page_id = {}
end
end
diff --git a/middleman-core/lib/middleman-core/util/paths.rb b/middleman-core/lib/middleman-core/util/paths.rb
index 61abec59..7c6ffc37 100644
--- a/middleman-core/lib/middleman-core/util/paths.rb
+++ b/middleman-core/lib/middleman-core/util/paths.rb
@@ -99,8 +99,13 @@ module Middleman
# 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.
- Contract ::Middleman::Application, Or[String, ::Middleman::Sitemap::Resource], Hash => String
+ Contract ::Middleman::Application, Or[String, Symbol, ::Middleman::Sitemap::Resource], Hash => String
def url_for(app, path_or_resource, options={})
+ if path_or_resource.is_a?(String) || path_or_resource.is_a?(Symbol)
+ r = app.sitemap.find_resource_by_page_id(path_or_resource)
+ path_or_resource = r if r
+ end
+
# Handle Resources and other things which define their own url method
url = if path_or_resource.respond_to?(:url)
path_or_resource.url