Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Semantic navigation for Rails

branch: master

This branch is 0 commits ahead and 0 commits behind master

Fetching latest commit…

Octocat-spinner-32-eaf2f5

Cannot retrieve the latest commit at this time

Octocat-spinner-32 lib
Octocat-spinner-32 test
Octocat-spinner-32 .document
Octocat-spinner-32 .gitignore
Octocat-spinner-32 LICENSE
Octocat-spinner-32 README.textile
Octocat-spinner-32 Rakefile
Octocat-spinner-32 VERSION
Octocat-spinner-32 navigasmic.gemspec
README.textile

Navigasmic

Semantic navigation; a semantic way to build beautifully simple navigation structures in Rails.

The Story

Most of the navigation styles I’ve done over the years pretty much boil down to this idea: Use simple markup, and do the rest with css (and javascript if you need it). So, yeah, the default markup is beautifully simple (UL and LI tags).

Ok, so navigation is easy right? Until you start managing active/highlighted, disabled states, and more if you may need them. This can quickly become a mess, and all too often it just stays in the views with whatever logic tossed on top of it as people go. I’ve seen it too many times, and I wanted to do something about it.

I went in with these requirements:

  • Should be simple
  • Should be easily customizable
  • Should handle active/highlighted states
  • Should handle disabled states
  • Should be pleasant to use
  • Should use less code to create than it generates

And then I wrote a DSL that met those requirements:

  <% semantic_navigation :main do |n| %>
    <%= n.group 'Media' do %>
      <%= n.item 'Image Gallery', :link => '/media/images', :highlights_on => '/media/videos' %>
      <%= n.item 'Videos', :link => '/media/videos', :disabled_if => proc { true } %>
      <%= n.item 'Contact Us' # auto links to the contact_us_path if it exists %>
    <% end %>
  <% end %>

Since I clearly needed something that allowed for customization I ended up emulating the way Rails uses form builders. With some ideas taken from formtastic, I setup navigasmic to use builders. There are two builders that are provided (HTML for ERB, HAML, etc. and XML for ERB, Builder, etc), and these builders can be extended or replaced if you need more custom markup.

Currently the HTML builder generates UL and LI tags, which helps with SEO, and the XML builder generates tags that can be used for XML site maps.

Installation

The gem is hosted on gemcutter, so if you haven’t already, add it as a gem source:

  sudo gem sources -a http://gemcutter.org/

Then install the navigasmic gem:

  sudo gem install navigasmic

Usage

HAML

Simple Usage

  - semantic_navigation :main do |n|
    = n.group 'Media', :html => {:class => 'media'} do
      = n.item 'Image Gallery', :link => {:controller => 'media/images'}
      = n.item 'Videos', :link => {:controller => 'media/videos'}
    = n.group 'Info' do
      = n.item 'About Us', :link => '/about_us', :html => {:title => 'we are the coolest'}
      = n.item 'Nothing Here'
…produces…
  <ul id="main" class="semantic-navigation">
    <li class="media with-group">
      <span>Media<span>
      <ul>
        <li id="image_gallery"><a href="/media/images"><span>Image Gallery</span></a></li>
        <li id="videos"><a href="/media/videos"><span>Videos</span></a></li>
      </ul>
    </li>
    <li class="with-group">
      <span>Info<span>
      <ul>
        <li id="about_us" title="we are the coolest"><a href="/about_us/"><span>About Us</span></a></li>
        <li id="videos"><span>Nothing Here</span></li>
      </ul>
    </li>
  </ul>

Nesting of items and additional markup

  - semantic_navigation :main, :html => { :class => 'primary' } do |n|
    = n.group 'Media' do
      = n.item 'Image Gallery', :link => {:controller => 'media/images', :action => 'index'}
      = n.item 'Videos', :link => {:controller => 'media/videos'}
    .secondary
      = n.group 'Info' do
        = n.item 'About Us', :link => '/about_us' do
          %ul
            = n.item 'Nothing Here'
…produces…
  <ul id="main" class="primary semantic-navigation">
    <li id="media" class="with-group">
      <span>Media</span>
      <ul>
        <li id="image_gallery"><a href="/media/images"><span>Image Gallery</span></a></li>
        <li id="videos"><a href="/media/videos"><span>Videos</span></a></li>
      </ul>
    </li>
    <div class="secondary">
      <li id="info" class="with-group">
        <span>Info</span>
        <ul>
          <li id="about_us">
            <a href="/about_us"><span>About Us</span></a>
            <ul>
              <li id="nothing_here"><span>Nothing Here</span></li>
            </ul>
          </li>
        </ul>
      </li>
    </div>
  </ul>

Highlighting — highlights on the media controller (or any that inherit from it), on the /images path, and on mondays

  - semantic_navigation :main do
    = n.item 'Image Gallery', :link => 'media/images'
            :highlights_on => [{:controller => 'media'}, '/images', proc { Time.now.wday == 1 }]
…produces
  <ul id="main" class="semantic-navigation">
    <li id="image_gallery" class="highlighted"><a href="/media/images"><span>Image Gallery</span></a></li>
  </ul>

Disabling — disabled on tuesdays, and when not logged in

  - semantic_navigation :main do
    = n.item 'Image Gallery', :link => 'media/images'
            :disabled_if => proc { Time.now.wday == 2 || !logged_in? }
…produces
  <ul id="main" class="semantic-navigation">
    <li id="image_gallery" class="disabled"><span>Image Gallery</span></li>
  </ul>

ERB

Nesting of items and additional markup

  <% semantic_navigation :main, :html => { :class => 'primary' } do |n| %>
    <%= n.group 'Media' do %>
      <%= n.item 'Image Gallery', :link => {:controller => 'media/images', :action => 'index'} %>
      <%= n.item 'Videos', :link => {:controller => 'media/videos'} %>
    <% end %>
    <div class="secondary">
      <%= n.group 'Info' do %>
        <%= n.item 'About Us', :link => '/about_us' do %>
          <ul><%= n.item 'Nothing Here' %></ul>
        <% end %>
      <% end %>
    </div>
  <% end %>
…produces…
  <ul id="main" class="primary semantic-navigation">
    <li id="media" class="with-group">
      <span>Media</span>
      <ul>
        <li id="image_gallery"><a href="/media/images"><span>Image Gallery</span></a></li>
        <li id="videos"><a href="/media/videos"><span>Videos</span></a></li>
      </ul>
    </li>
    <div class="secondary">
      <li id="info" class="with-group">
        <span>Info</span>
        <ul>
          <li id="about_us">
            <a href="/about_us"><span>About Us</span></a>
            <ul>
              <li id="nothing_here"><span>Nothing Here</span></li>
            </ul>
          </li>
        </ul>
      </li>
    </div>
  </ul>  

XML

The XML Builder isn’t hashed out completely, but it more or less generates google sitemap style xml. I don’t know if the format is entirely correct in terms of nesting (it would make sense if it is), and I’d invite anyone who would like to hash it out more to do so.

Simple Usage Using Builder

  xml.instruct! :xml, :version => '1.0', :encoding => 'UTF-8'
  semantic_navigation :primary, :builder => Navigasmic::XmlNavigationBuilder do |n|
    n.group 'Media' do
      n.item 'Image Gallery', :link => {:controller => 'media/images', :action => 'index'}
      n.item 'Videos', :link => {:controller => 'media/videos'}, :changefreq => 'monthly'
    end
	end
…produces…
  <?xml version="1.0" encoding="UTF-8"?>
  <urlset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">
    <urlset>
      <url>
        <loc>http://host:port/media/images</loc>
        <changefreq>yearly</changefreq>
      </url>
      <url>
        <loc>http://host:port/media/videos</loc>
        <changefreq>monthly</changefreq>
      </url>
    </urlset>
  </urlset>

Simple Usage Using HAML

  - semantic_navigation :primary, :builder => Navigasmic::XmlNavigationBuilder, :changefreq => 'daily' do |n|
    = n.item 'Image Gallery', :link => {:controller => 'media/images', :action => 'index'}
    = n.item 'Videos', :link => {:controller => 'media/videos'}, :changefreq => 'monthly'
…produces…
  <urlset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">
    <url>
      <loc>http://host:port/media/images</loc>
      <changefreq>daily</changefreq>
    </url>
    <url>
      <loc>http://host:port/media/videos</loc>
      <changefreq>monthly</changefreq>
    </url>
  </urlset>

Tip: You can check the template_format and use the XML Builder when it’s :xml, and the HTML Builder if not.
:builder => template_format == :xml ? Navigasmic::XmlNavigationBuilder : nil

Other

You can always create your own builder by extending one of the existing ones, or creating one from scratch.

Then just specify your builder, or do it as a configuration.

semantic_navigation :main, :builder => MyCustomBuilder do

Navigasmic::SemanticNavigationHelper.builder = MyCustomBuilder

Documentation

RDoc documentation should be automatically generated after each commit and made available on the rdoc.info website.

Compatibility

I’m only testing with the latest Rails 3.x stable release, and it should work under Rails 2.3.x as well. Feel free to add backwards compatibility if you need it.

Project Info

Navigasmic is hosted on Github: http://github.com/jejacks0n/navigasmic, and the gem is available on Gemcutter: http://gemcutter.org/gems/navigasmic

Copyright © Jeremy Jackson, released under the MIT license.

Something went wrong with that request. Please try again.