0

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

http://rubydoc.info/github/middleman/middleman/Middleman/Sitemap/Extensions/Traversal#siblings-instance_method

Leahcim
  • 40,649
  • 59
  • 195
  • 334

1 Answers1

8

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.

Brian Kung
  • 3,957
  • 4
  • 21
  • 30
  • 1
    Thank you so much. This is awesome. I'm going to look at this and get back to you – Leahcim Mar 20 '13 at 03:40
  • For what it's worth, I was able to dig into the source code really easily by cloning the middleman source and then navigating it with RubyMine's automatic method definition lookups. – Brian Kung Mar 20 '13 at 20:07
  • ok, I know how to organize the files, but I'm having trouble creating a navigation using parent, children, sibling methods to show the structure of a site. Any tips on how you'd write that? – Leahcim Mar 21 '13 at 07:47
  • This is off the top of my head, but how about calling #children on the root directory and all of its children, recursively? That way you could map out the children of all the children of the top level directory. Also, to clarify, you are trying to dynamically generate a navigation bar? – Brian Kung Mar 21 '13 at 18:31
  • Thanks, yes a nav bar, however, one that serves as a visual map of the content of the site, which is to say that it shows parent, sibling, children relationships between files. – Leahcim Mar 21 '13 at 23:01
  • That's an interesting data visualization problem. A bit beyond the scope of my expertise, though! Good luck with that one. – Brian Kung Mar 22 '13 at 00:47