tiny_navigation 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Coroutine
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,107 @@
1
+ = Tiny Navigation
2
+
3
+ == Installation
4
+
5
+ === As a Plugin
6
+
7
+ * For Rails 2.x
8
+
9
+ script/generate plugin install git@github.com:coroutine/tiny_navigation.git
10
+
11
+ === As a gem
12
+
13
+ gem install tiny_navigation
14
+
15
+
16
+ Then, simply call:
17
+
18
+ script/generate tiny_navigation
19
+
20
+ This will place the tiny_navigation.rb config file into your config directory.
21
+
22
+ == Usage
23
+
24
+ TinyNavigation provides an easy-to-use DSL for defining navigation structures;
25
+ these structures are defined in config/tiny_navigation.rb.
26
+
27
+ === Here are a few things TinyNavigation WILL do:
28
+
29
+ * It provides the ability to define and map menu items to resources using the
30
+ convention set forth in the Rails 3 router. For example, to map a menu item
31
+ _Foo_ to the <tt>show</tt> action of the <tt>foos_controller</tt> simply do:
32
+
33
+ navigation :top_tabs do
34
+ item "Foo", :to => "foos#show"
35
+ end
36
+
37
+ If one were to omit the specified action from the <tt>:to</tt> option, the
38
+ navigation item's action would default to <tt>index</tt>.
39
+
40
+ The URL generated from this mapping can be accessed via the <tt>url</tt>
41
+ method of the navigation item.
42
+
43
+ * It provides a <tt>selected</tt> method for getting the selected menu items of
44
+ a navigational structure. For example, for this definition:
45
+
46
+ navigation :main do
47
+ item "Foo", :to => "foos#index"
48
+ item "Bar", :to => "bars#index" do
49
+ item "Baz", :to => "bazzs#index"
50
+ end
51
+ end
52
+
53
+ If the menu item _Foo_ is selected, an array containing that menu item is
54
+ returned. However, if the menu item _Baz_ is selected, an array containing
55
+ the _Bar_ and the _Baz_ menu items. This is useful for generating bread-crumbs
56
+ or simply highlighting both the main nav item and its selected sub-nav item.
57
+
58
+ * It allows for the declaration of custom attributes on nav items. For instance,
59
+ given the configuration in the previous example, we want to right-align the _Bar_
60
+ navigation item. To do this we could simply add another option to the item:
61
+
62
+ navigation :main do
63
+ item "Foo", :to => "foos#index", :align => :left
64
+ item "Bar", :to => "bars#index", :align => :right do
65
+ item "Baz", :to => "bazzs#index"
66
+ end
67
+ end
68
+
69
+ Now, when we render the navigation items we can call <tt>align</tt> on
70
+ the item to get its value:
71
+
72
+ navigation(:main).each do |item|
73
+ if item.align == :right
74
+ ...
75
+ end
76
+ end
77
+
78
+ * It delegates controller method calls made from within the config file to the
79
+ current controller. For instance, let's say you're using Ryan Bates' fantastic
80
+ CanCan gem for authorization--which adds a some methods to the controller, namely
81
+ the <tt>can?</tt> method--and you want to show or hide navigation items based upon
82
+ a user's ability. You can do that! Check it:
83
+
84
+ navigation :main do
85
+ item("Foo", :to => "foos#index") if can? :read, Foo
86
+ item "Bar", :to => "bars#index" do
87
+ item "Baz", :to => "bazzs#index"
88
+ end
89
+ end
90
+
91
+ *IMPORTANT* if a custom attribute is defined on an item, as mentioned earlier,
92
+ it will take precedence over a controller attribute of the same name, thus
93
+ hiding access to the controller attribute.
94
+
95
+ === Here are a couple things that TinyNavigation WILL NOT do:
96
+
97
+ * TinyNavigation makes no attempt at rendering the navigation. That's up
98
+ to you. You may want to render your nav items into <tt>div</tt> tags, while
99
+ I may want to use an unordered list. That's fine, go for it.
100
+
101
+ * TinyNavigation does provide authorization logic for limiting access to
102
+ navigation items; that's a separate concern. It's easy enough to use
103
+ an authorization gem that does that job quite well, and by allowing for calls
104
+ to the current controller from within config/tiny_navigation.rb, you can
105
+ do that.
106
+
107
+ Copyright (c) 2010 Coroutine, released under the MIT license
data/Rakefile ADDED
@@ -0,0 +1,38 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+
5
+ desc 'Default: run unit tests.'
6
+ task :default => :test
7
+
8
+ desc 'Test the tiny_navigation plugin.'
9
+ Rake::TestTask.new(:test) do |t|
10
+ t.libs << 'lib'
11
+ t.libs << 'test'
12
+ t.pattern = 'test/**/*_test.rb'
13
+ t.verbose = true
14
+ end
15
+
16
+ desc 'Generate documentation for the tiny_navigation plugin.'
17
+ Rake::RDocTask.new(:rdoc) do |rdoc|
18
+ rdoc.rdoc_dir = 'rdoc'
19
+ rdoc.title = 'TinyNavigation'
20
+ rdoc.options << '--line-numbers' << '--inline-source'
21
+ rdoc.rdoc_files.include('README')
22
+ rdoc.rdoc_files.include('lib/**/*.rb')
23
+ end
24
+
25
+ begin
26
+ require 'jeweler'
27
+ Jeweler::Tasks.new do |gemspec|
28
+ gemspec.name = "tiny_navigation"
29
+ gemspec.summary = "TinyNavigation provides an easy-to-use DSL for defining navigation structures."
30
+ gemspec.description = "TinyNavigation makes it easy to define site navigation using a small DSL."
31
+ gemspec.email = "gems@coroutine.com"
32
+ gemspec.homepage = "http://github.com/coroutine/tiny_navigation"
33
+ gemspec.authors = ["Coroutine", "Tim Lowrimore"]
34
+ end
35
+ Jeweler::GemcutterTasks.new
36
+ rescue LoadError
37
+ puts "Jeweler not available. Install it with: gem install jeweler"
38
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,5 @@
1
+ Call:
2
+
3
+ rails generate tiny_navigation
4
+
5
+ to generate the config/tiny_navigation.rb file.
@@ -0,0 +1,11 @@
1
+ # Within this file you'll define your navigation data.
2
+
3
+ # navigation :main do
4
+ # # Menu Item
5
+ # item "Foos", :to => "foos#index"
6
+ #
7
+ # # Menu Item with a sub-menu item
8
+ # item "Bars", :to => "bars#index" do
9
+ # item "Bazzes", :to => "bazzes#index"
10
+ # end
11
+ # end
@@ -0,0 +1,7 @@
1
+ class TinyNavigationGenerator < Rails::Generator::Base
2
+ def manifest
3
+ record do |m|
4
+ m.file "tiny_navigation.rb", "config/tiny_navigation.rb"
5
+ end
6
+ end
7
+ end
data/init.rb ADDED
@@ -0,0 +1,2 @@
1
+ require "tiny_navigation"
2
+ ActionController::Base.class_eval { include Coroutine::TinyNavigation::ControllerMethods }
@@ -0,0 +1,128 @@
1
+ require 'tiny_navigation/item'
2
+ require 'tiny_navigation/navigation'
3
+
4
+ module Coroutine
5
+
6
+ # TinyNavigation provides an easy-to-use DSL for defining navigation structures;
7
+ # these structures are defined in config/tiny_navigation.rb.
8
+ #
9
+ # == Here are a few things TinyNavigation WILL do:
10
+ #
11
+ # * It provides the ability to define and map menu items to resources using the
12
+ # convention set forth in the Rails 3 router. For example, to map a menu item
13
+ # _Foo_ to the <tt>show</tt> action of the <tt>foos_controller</tt> simply do:
14
+ #
15
+ # navigation :top_tabs do
16
+ # item "Foo", :to => "foos#show"
17
+ # end
18
+ #
19
+ # If one were to omit the specified action from the <tt>:to</tt> option, the
20
+ # navigation item's action would default to <tt>index</tt>.
21
+ #
22
+ # The URL generated from this mapping can be accessed via the <tt>url</tt>
23
+ # method of the navigation item.
24
+ #
25
+ # * It provides a <tt>selected</tt> method for getting the selected menu items of
26
+ # a navigational structure. For example, for this definition:
27
+ #
28
+ # navigation :main do
29
+ # item "Foo", :to => "foos#index"
30
+ # item "Bar", :to => "bars#index" do
31
+ # item "Baz", :to => "bazzs#index"
32
+ # end
33
+ # end
34
+ #
35
+ # If the menu item _Foo_ is selected, an array containing that menu item is
36
+ # returned. However, if the menu item _Baz_ is selected, an array containing
37
+ # the _Bar_ and the _Baz_ menu items. This is useful for generating bread-crumbs
38
+ # or simply highlighting both the main nav item and its selected sub-nav item.
39
+ #
40
+ # * It allows for the declaration of custom attributes on nav items. For instance,
41
+ # given the configuration in the previous example, we want to right-align the _Bar_
42
+ # navigation item. To do this we could simply add another option to the item:
43
+ #
44
+ # navigation :main do
45
+ # item "Foo", :to => "foos#index", :align => :left
46
+ # item "Bar", :to => "bars#index", :align => :right do
47
+ # item "Baz", :to => "bazzs#index"
48
+ # end
49
+ # end
50
+ #
51
+ # Now, when we render the navigation items we can call <tt>right_align</tt> on
52
+ # the item to get its value:
53
+ #
54
+ # navigation(:main).each do |item|
55
+ # if item.align == :right
56
+ # ...
57
+ # end
58
+ # end
59
+ #
60
+ # * It delegates controller method calls made from within the config file to the
61
+ # current controller. For instance, let's say you're using Ryan Bates' fantastic
62
+ # CanCan gem for authorization--which adds a some methods to the controller, namely
63
+ # the <tt>can?</tt> method--and you want to show or hide navigation items based upon
64
+ # a user's ability. You can do that! Check it:
65
+ #
66
+ # navigation :main do
67
+ # item("Foo", :to => "foos#index") if can? :read, Foo
68
+ # item "Bar", :to => "bars#index" do
69
+ # item "Baz", :to => "bazzs#index"
70
+ # end
71
+ # end
72
+ #
73
+ # *IMPORTANT* if a custom attribute is defined on an item, as mentioned earlier,
74
+ # it will take precedence over a controller attribute of the same name, thus
75
+ # hiding access to the controller attribute.
76
+ #
77
+ # == Here are a couple things that TinyNavigation WILL NOT do:
78
+ #
79
+ # * TinyNavigation makes no attempt at rendering the navigation. That's up
80
+ # to you. You may want to render your nav items into <tt>div</tt> tags, while
81
+ # I may want to use an unordered list. That's fine, go for it.
82
+ #
83
+ # * TinyNavigation does provide authorization logic for limiting access to
84
+ # navigation items; that's a separate concern. It's easy enough to use
85
+ # an authorization gem that does that job quite well, and by allowing for calls
86
+ # to the current controller from within config/tiny_navigation.rb you can
87
+ # do that.
88
+ module TinyNavigation
89
+ class Config #:nodoc:
90
+
91
+ attr_reader :nav
92
+
93
+ def initialize(current_controller, conf=File.join(Rails.root, "config", "tiny_navigation.rb"))
94
+ @current_controller = current_controller
95
+ @nav = {}
96
+
97
+ # Make sure we only load the config file the first time the navigation is loaded
98
+ # and never again.
99
+ Config.class_eval { class << self; attr_reader :file end; @file ||= File.read(conf) }
100
+ self.instance_eval(Config.file);
101
+ end
102
+
103
+ private
104
+
105
+ def navigation(name, &block)
106
+ raise "Navigation names must be unique. You specified '#{name}' twice." if @nav.has_key?(name)
107
+ @nav[name] = Navigation.new(name, @current_controller, &block)
108
+ end
109
+ end
110
+
111
+ # This module adds the navigation method to ActionController::Base and makes
112
+ # the method available as a helper.
113
+ module ControllerMethods
114
+ def self.included(base) #:nodoc:
115
+ base.send :helper_method, :navigation
116
+ end
117
+
118
+ private
119
+
120
+ # Returns a Coroutine::TinyNavigation::Navigation object for the supplied
121
+ # navigation name.
122
+ def navigation(which_navigation)
123
+ config = Coroutine::TinyNavigation::Config.new self
124
+ config.nav[which_navigation]
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,66 @@
1
+ require 'tiny_navigation/navigation'
2
+
3
+ module Coroutine
4
+ module TinyNavigation
5
+ class Item < Navigation
6
+
7
+ # Creates a new instance of a navigation item.
8
+ #
9
+ # <tt>name</tt> is the name of the navigation item. The name should
10
+ # be used for the label text when rendering the navigation item.
11
+ #
12
+ # <tt>current_controller</tt> the currently loaded controller
13
+ #
14
+ # <tt>options</tt> provide configuration options and custom properties
15
+ # to the nav item. Currently, the only configuration option is
16
+ # <tt>:to</tt> which is used to generate the URL of the navigation item.
17
+ # All other options provided to via the <tt>options</tt> hash will be
18
+ # treated as custom properties on the navigation item. These custom
19
+ # properties can be accessed as methods on the navigation item.
20
+ #
21
+ # The block given to the navigation item is used to define sub-navigation
22
+ # items of this item.
23
+ def initialize(name, current_controller, options={}, &block)
24
+ super name, current_controller, &block
25
+ set_controller_and_action options.delete(:to)
26
+ @extra_options = options
27
+ end
28
+
29
+ # Indicates whether the navigation item is currently selected. This
30
+ # takes into account any sub-nav items such that a parent item is selected
31
+ # if it has a selected child.
32
+ def selected?
33
+ @controller_name == @current_controller.controller_name || @items.any?(&:selected?)
34
+ end
35
+
36
+ # Returns the URL to which the navigation item points. This should be
37
+ # used in a scenario where the navigation item represents a link and the
38
+ # URL is the href of that link.
39
+ def url
40
+ @current_controller.url_for :controller => @controller_name, :action => @action_name
41
+ end
42
+
43
+ def method_missing(method_name, *args) #:nodoc:
44
+
45
+ # The extra_options hash takes precendence when looking for the
46
+ # called method. Otherwise, we'll let the super-class forward
47
+ # the method call to the current controller.
48
+ if @extra_options.has_key? method_name
49
+ @extra_options[method_name]
50
+ else
51
+ super method_name, *args
52
+ end
53
+ end
54
+
55
+ private
56
+
57
+ def set_controller_and_action(to)
58
+ if to
59
+ controller_and_action = to.split "#"
60
+ @controller_name = controller_and_action.shift
61
+ @action_name = controller_and_action.shift || "index"
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,47 @@
1
+ module Coroutine
2
+ module TinyNavigation
3
+ class Navigation
4
+
5
+ attr_reader :name, :items
6
+
7
+ # Creates a new navigation.
8
+ #
9
+ # <tt>name</tt> is the unique identifer of the navigation.
10
+ #
11
+ # <tt>current_controller</tt> the currently loaded controller
12
+ #
13
+ # The block given to the navigation item is used to define navigation
14
+ # items of this navigation object.
15
+ def initialize(name, current_controller, &block)
16
+ @items = []
17
+ @name = name
18
+ @current_controller = current_controller
19
+
20
+ self.instance_eval(&block) if block_given?
21
+ end
22
+
23
+ # Returns an array of selected navigation items. The array represents a
24
+ # bread-crumb list in that the head of the list represents a top-level
25
+ # navigation item, and the tail of the list represents selected sub-navigation
26
+ # items.
27
+ def selected(item=self)
28
+ items = []
29
+ item.items.each do |item|
30
+ items << item << selected(item) if item.selected?
31
+ end
32
+ items.flatten
33
+ end
34
+
35
+ def method_missing(method_name, *args) #:nodoc:
36
+ @current_controller.send method_name, *args
37
+ end
38
+
39
+ private
40
+
41
+ def item(name, options={}, &block)
42
+ @items << Item.new(name, @current_controller, options, &block)
43
+ end
44
+
45
+ end
46
+ end
47
+ end
Binary file
@@ -0,0 +1,54 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{tiny_navigation}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Coroutine", "Tim Lowrimore"]
12
+ s.date = %q{2010-05-13}
13
+ s.description = %q{TinyNavigation makes it easy to define site navigation using a small DSL.}
14
+ s.email = %q{gems@coroutine.com}
15
+ s.extra_rdoc_files = [
16
+ "README.rdoc"
17
+ ]
18
+ s.files = [
19
+ "MIT-LICENSE",
20
+ "README.rdoc",
21
+ "Rakefile",
22
+ "VERSION",
23
+ "generators/tiny_navigation/USAGE",
24
+ "generators/tiny_navigation/tiny_navigation_generator.rb",
25
+ "generators/tiny_navigation/templates/tiny_navigation.rb",
26
+ "init.rb",
27
+ "lib/tiny_navigation.rb",
28
+ "lib/tiny_navigation/item.rb",
29
+ "lib/tiny_navigation/navigation.rb",
30
+ "test/navigation_test.rb",
31
+ "test/test_helper.rb",
32
+ "uninstall.rb"
33
+ ]
34
+ s.homepage = %q{http://github.com/coroutine/tiny_navigation}
35
+ s.rdoc_options = ["--charset=UTF-8"]
36
+ s.require_paths = ["lib"]
37
+ s.rubygems_version = %q{1.3.6}
38
+ s.summary = %q{TinyNavigation provides an easy-to-use DSL for defining navigation structures.}
39
+ s.test_files = [
40
+ "test/navigation_test.rb",
41
+ "test/test_helper.rb"
42
+ ]
43
+
44
+ if s.respond_to? :specification_version then
45
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
46
+ s.specification_version = 3
47
+
48
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
49
+ else
50
+ end
51
+ else
52
+ end
53
+ end
54
+
@@ -0,0 +1,49 @@
1
+ require 'test_helper'
2
+ require 'tiny_navigation/navigation'
3
+ require 'tiny_navigation/item'
4
+
5
+ class NavigationTest < ActiveSupport::TestCase
6
+
7
+ def setup
8
+ @controller_class = Struct.new(:controller_name)
9
+ @current_controller = @controller_class.new("tests")
10
+ end
11
+
12
+ test "can create a navigation with an item" do
13
+ nav = navigation do
14
+ item "Foos", :to => "foos#index"
15
+ end
16
+
17
+ assert_equal nav.items.length, 1
18
+ end
19
+
20
+ test "has correct selected item in one level of nesting" do
21
+ nav = navigation do
22
+ item "Tests", :to => "tests"
23
+ end
24
+
25
+ assert_equal nav.selected.length, 1
26
+ assert_equal nav.selected.first.name, "Tests"
27
+ end
28
+
29
+ test "has correct selected items in multiple levels of nesting" do
30
+ nav = navigation do
31
+ item "Foos", :to => "foos#index" do
32
+ item "Tests", :to => "tests#index"
33
+ end
34
+ end
35
+
36
+ assert_equal nav.selected.length, 2
37
+ assert_equal nav.selected.map(&:name), ["Foos", "Tests"]
38
+ end
39
+
40
+ # ------------------------------------------------------------------------------
41
+ # Helpers
42
+ # ------------------------------------------------------------------------------
43
+
44
+ private
45
+
46
+ def navigation(name = :main, current_controller = @current_controller, &block)
47
+ Coroutine::TinyNavigation::Navigation.new(name, current_controller, &block)
48
+ end
49
+ end
@@ -0,0 +1,3 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'active_support'
data/uninstall.rb ADDED
@@ -0,0 +1 @@
1
+ # Uninstall hook code here
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tiny_navigation
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - Coroutine
13
+ - Tim Lowrimore
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-05-13 00:00:00 -05:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: TinyNavigation makes it easy to define site navigation using a small DSL.
23
+ email: gems@coroutine.com
24
+ executables: []
25
+
26
+ extensions: []
27
+
28
+ extra_rdoc_files:
29
+ - README.rdoc
30
+ files:
31
+ - MIT-LICENSE
32
+ - README.rdoc
33
+ - Rakefile
34
+ - VERSION
35
+ - generators/tiny_navigation/USAGE
36
+ - generators/tiny_navigation/templates/tiny_navigation.rb
37
+ - generators/tiny_navigation/tiny_navigation_generator.rb
38
+ - init.rb
39
+ - lib/tiny_navigation.rb
40
+ - lib/tiny_navigation/item.rb
41
+ - lib/tiny_navigation/navigation.rb
42
+ - pkg/simple_navigation-0.1.0.gem
43
+ - simple_navigation.gemspec
44
+ - test/navigation_test.rb
45
+ - test/test_helper.rb
46
+ - uninstall.rb
47
+ has_rdoc: true
48
+ homepage: http://github.com/coroutine/tiny_navigation
49
+ licenses: []
50
+
51
+ post_install_message:
52
+ rdoc_options:
53
+ - --charset=UTF-8
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ segments:
61
+ - 0
62
+ version: "0"
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ segments:
68
+ - 0
69
+ version: "0"
70
+ requirements: []
71
+
72
+ rubyforge_project:
73
+ rubygems_version: 1.3.6
74
+ signing_key:
75
+ specification_version: 3
76
+ summary: TinyNavigation provides an easy-to-use DSL for defining navigation structures.
77
+ test_files:
78
+ - test/navigation_test.rb
79
+ - test/test_helper.rb