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...
- In short, it seems like the parent resource can be found one level up with the file name 'index.html,' so if you're viewing
/foo/bar/some_resource.html
, its parent can be found at /foo/index.html
.
- Its siblings are at the same level as the request (note that "eponymous directories" get converted into an 'index file', eg.
/foo/bar/
becomes /foo/bar.html
, making its siblings anything at the /foo/*
level.)
- Its children are at the level below.
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.
A deeper dive
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.