When creating pages in middleman, how do I indicate which pages are parents/siblings/children? The documentation gives some indication how you can use parent siblings and children methods to build navigation and breadcrumbs, but it doesn't say how to arrange the pages in the directory so that they respond to these methods (parent, siblings, children) in the appropriate way.
Each resource in the sitemap is a Resource object. Pages can tell you all kinds of interesting things about themselves. You can access frontmatter data, file extension, source and output paths, a linkable url, its mime type, etc. Some of the properties of the Page are mostly useful for Middleman's rendering internals, but you could imagine filtering pages on file extension to find all .html files, for example.
Each page can also find other pages related to it in the site hierarchy. The parent, siblings, and children methods are particularly useful in building navigation menus and breadcrumbs.
This is the parent method http://rubydoc.info/github/middleman/middleman/Middleman/Sitemap/Extensions/Traversal#parent-instance_method
This is the the children method http://rubydoc.info/github/middleman/middleman/Middleman/Sitemap/Extensions/Traversal#children-instance_method
This is the siblings method
After a bit of spelunking into Middleman's code, I found a Cucumber test that describes the #parent
, #children
, and #siblings
methods.
middleman / middleman-core / features / sitemap_traversal.feature:
Feature: Step through sitemap as a tree
Scenario: Root
Given the Server is running at "traversal-app"
When I go to "/index.html"
Then I should see "Path: index.html"
Then I should not see "Parent: index.html"
Then I should see "Child: sub/index.html"
Then I should see "Child: root.html"
Then I should not see "Child: proxied.html"
...continued...
/foo/bar/some_resource.html
, its parent can be found at /foo/index.html
. /foo/bar/
becomes /foo/bar.html
, making its siblings anything at the /foo/*
level.)By placing any file in their respective positions in your file hierarchy, you should be able to reference that file, or a set which includes that file, by calling #parent
, #children
, or #siblings
.
Be warned while reading the tests that there are a couple "fake" routes (/sub/fake.html
and fake2.html
, and /directory-indexed/fake.html
and fake2.html
) that have been set up in the config file.
If you can't take the Cucumber tests at face value (and I don't blame you), there's more! After all, what is this, "I should see" and "I should not see" nonsense? Well, there's an answer for that.
In the fixtures for the test (middleman/middleman-core/fixtures/traversal-app/), layout.erb is the only file with any contents whatsoever. In it, you can see that the Child, Parent, and Sibling paths are printed out in the body of the html document.
middleman / middleman-core / fixtures / traversal-app / source / layout.erb:
Path: <%= current_page.path %>
Source: <%= current_page.source_file.sub(root + "/", "") %>
<% if current_page.parent %>
Parent: <%= current_page.parent.path %>
<% end %>
...continued...
This helps to explain the tests, which are simply looking for strings in the response body, originating from the layout. You can see the test's exact behavior in the Cucumber step definitions (middleman / middleman-core / lib / middleman-core / step_definitions /).
Finally, the layout uses the methods that we set out to describe in the first place, #parent
, #children
, and #siblings
, which are defined in middleman-core.
In middleman / middleman-core / lib / middleman-core / sitemap / extensions / traversal.rb:
module Traversal
# This resource's parent resource
# @return [Middleman::Sitemap::Resource, nil]
def parent
parts = path.split("/")
parts.pop if path.include?(app.index_file)
return nil if parts.length < 1
parts.pop
parts << app.index_file
parent_path = "/" + parts.join("/")
store.find_resource_by_destination_path(parent_path)
end
# This resource's child resources
# @return [Array<Middleman::Sitemap::Resource>]
def children
return [] unless directory_index?
if eponymous_directory?
base_path = eponymous_directory_path
prefix = %r|^#{base_path.sub("/", "\\/")}|
else
base_path = path.sub("#{app.index_file}", "")
prefix = %r|^#{base_path.sub("/", "\\/")}|
end
store.resources.select do |sub_resource|
if sub_resource.path == self.path || sub_resource.path !~ prefix
false
else
inner_path = sub_resource.path.sub(prefix, "")
parts = inner_path.split("/")
if parts.length == 1
true
elsif parts.length == 2
parts.last == app.index_file
else
false
end
end
end
end
# This resource's sibling resources
# @return [Array<Middleman::Sitemap::Resource>]
def siblings
return [] unless parent
parent.children.reject { |p| p == self }
end
...continued...
end
I'll leave off explaining the Ruby code, as I've got to hurry off to work. If you'd like me to investigate further, let me know in the comments and I'll get back at it.