menumatic 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/.DS_Store +0 -0
- data/.autotest +1 -0
- data/.gitignore +3 -0
- data/.rspec +2 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +95 -0
- data/LICENSE +24 -0
- data/README.md +144 -0
- data/Rakefile +2 -0
- data/TODO.md +36 -0
- data/app/controllers/menumatic/sitemap_controller.rb +5 -0
- data/app/views/menumatic/sitemap/index.xml.builder +4 -0
- data/lib/.DS_Store +0 -0
- data/lib/generators/menumatic/USAGE +3 -0
- data/lib/generators/menumatic/navigation_generator.rb +23 -0
- data/lib/generators/menumatic/templates/navigation.rb +3 -0
- data/lib/generators/menumatic/templates/stylesheet.css +73 -0
- data/lib/menumatic/.DS_Store +0 -0
- data/lib/menumatic/engine.rb +4 -0
- data/lib/menumatic/helpers/navigation_helper.rb +36 -0
- data/lib/menumatic/navigation/.DS_Store +0 -0
- data/lib/menumatic/navigation/item/group.rb +13 -0
- data/lib/menumatic/navigation/item/link.rb +19 -0
- data/lib/menumatic/navigation/item/navigators.rb +17 -0
- data/lib/menumatic/navigation/item/renderers.rb +109 -0
- data/lib/menumatic/navigation/item.rb +31 -0
- data/lib/menumatic/navigation.rb +55 -0
- data/lib/menumatic/rails/routes.rb +7 -0
- data/lib/menumatic/version.rb +3 -0
- data/lib/menumatic.rb +23 -0
- data/menumatic.gemspec +21 -0
- data/spec/factories.rb +6 -0
- data/spec/link_spec.rb +52 -0
- data/spec/spec_helper.rb +9 -0
- metadata +103 -0
data/.DS_Store
ADDED
Binary file
|
data/.autotest
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'autotest/growl'
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
menumatic (0.0.1)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: http://rubygems.org/
|
8
|
+
specs:
|
9
|
+
ZenTest (4.5.0)
|
10
|
+
abstract (1.0.0)
|
11
|
+
actionmailer (3.0.5)
|
12
|
+
actionpack (= 3.0.5)
|
13
|
+
mail (~> 2.2.15)
|
14
|
+
actionpack (3.0.5)
|
15
|
+
activemodel (= 3.0.5)
|
16
|
+
activesupport (= 3.0.5)
|
17
|
+
builder (~> 2.1.2)
|
18
|
+
erubis (~> 2.6.6)
|
19
|
+
i18n (~> 0.4)
|
20
|
+
rack (~> 1.2.1)
|
21
|
+
rack-mount (~> 0.6.13)
|
22
|
+
rack-test (~> 0.5.7)
|
23
|
+
tzinfo (~> 0.3.23)
|
24
|
+
activemodel (3.0.5)
|
25
|
+
activesupport (= 3.0.5)
|
26
|
+
builder (~> 2.1.2)
|
27
|
+
i18n (~> 0.4)
|
28
|
+
activerecord (3.0.5)
|
29
|
+
activemodel (= 3.0.5)
|
30
|
+
activesupport (= 3.0.5)
|
31
|
+
arel (~> 2.0.2)
|
32
|
+
tzinfo (~> 0.3.23)
|
33
|
+
activeresource (3.0.5)
|
34
|
+
activemodel (= 3.0.5)
|
35
|
+
activesupport (= 3.0.5)
|
36
|
+
activesupport (3.0.5)
|
37
|
+
arel (2.0.9)
|
38
|
+
autotest (4.4.6)
|
39
|
+
ZenTest (>= 4.4.1)
|
40
|
+
autotest-growl (0.2.9)
|
41
|
+
builder (2.1.2)
|
42
|
+
diff-lcs (1.1.2)
|
43
|
+
erubis (2.6.6)
|
44
|
+
abstract (>= 1.0.0)
|
45
|
+
factory_girl (1.3.2)
|
46
|
+
i18n (0.5.0)
|
47
|
+
mail (2.2.15)
|
48
|
+
activesupport (>= 2.3.6)
|
49
|
+
i18n (>= 0.4.0)
|
50
|
+
mime-types (~> 1.16)
|
51
|
+
treetop (~> 1.4.8)
|
52
|
+
mime-types (1.16)
|
53
|
+
polyglot (0.3.1)
|
54
|
+
rack (1.2.1)
|
55
|
+
rack-mount (0.6.13)
|
56
|
+
rack (>= 1.0.0)
|
57
|
+
rack-test (0.5.7)
|
58
|
+
rack (>= 1.0)
|
59
|
+
rails (3.0.5)
|
60
|
+
actionmailer (= 3.0.5)
|
61
|
+
actionpack (= 3.0.5)
|
62
|
+
activerecord (= 3.0.5)
|
63
|
+
activeresource (= 3.0.5)
|
64
|
+
activesupport (= 3.0.5)
|
65
|
+
bundler (~> 1.0)
|
66
|
+
railties (= 3.0.5)
|
67
|
+
railties (3.0.5)
|
68
|
+
actionpack (= 3.0.5)
|
69
|
+
activesupport (= 3.0.5)
|
70
|
+
rake (>= 0.8.7)
|
71
|
+
thor (~> 0.14.4)
|
72
|
+
rake (0.8.7)
|
73
|
+
rspec (2.5.0)
|
74
|
+
rspec-core (~> 2.5.0)
|
75
|
+
rspec-expectations (~> 2.5.0)
|
76
|
+
rspec-mocks (~> 2.5.0)
|
77
|
+
rspec-core (2.5.1)
|
78
|
+
rspec-expectations (2.5.0)
|
79
|
+
diff-lcs (~> 1.1.2)
|
80
|
+
rspec-mocks (2.5.0)
|
81
|
+
thor (0.14.6)
|
82
|
+
treetop (1.4.9)
|
83
|
+
polyglot (>= 0.3.1)
|
84
|
+
tzinfo (0.3.24)
|
85
|
+
|
86
|
+
PLATFORMS
|
87
|
+
ruby
|
88
|
+
|
89
|
+
DEPENDENCIES
|
90
|
+
autotest
|
91
|
+
autotest-growl
|
92
|
+
factory_girl
|
93
|
+
menumatic!
|
94
|
+
rails (~> 3.0.4)
|
95
|
+
rspec
|
data/LICENSE
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
Open Source Initiative OSI - The MIT License:Licensing
|
2
|
+
|
3
|
+
The MIT License
|
4
|
+
|
5
|
+
Copyright (c) 2011 Nicholas Bruning
|
6
|
+
|
7
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
8
|
+
of this software and associated documentation files (the "Software"), to deal
|
9
|
+
in the Software without restriction, including without limitation the rights
|
10
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
11
|
+
copies of the Software, and to permit persons to whom the Software is
|
12
|
+
furnished to do so, subject to the following conditions:
|
13
|
+
|
14
|
+
The above copyright notice and this permission notice shall be included in
|
15
|
+
all copies or substantial portions of the Software.
|
16
|
+
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
18
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
19
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
20
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
21
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
22
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
23
|
+
THE SOFTWARE.
|
24
|
+
|
data/README.md
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
# Menumatic
|
2
|
+
|
3
|
+
Menumatic is a _Rails 3 exclusive_ gem which aids in developing HTML
|
4
|
+
navigation menus.
|
5
|
+
|
6
|
+
|
7
|
+
## Philosophy
|
8
|
+
|
9
|
+
When designing Menumatic, I wanted a way in which I could remove
|
10
|
+
navigation logic from the view all together, but also keep configuration
|
11
|
+
to a bare minimum and (hopefully) stick to the principle of
|
12
|
+
'convention-over-configuration'.
|
13
|
+
|
14
|
+
Menumatic considers navigations to be more like models as opposed to
|
15
|
+
views. As such, navigations are defined in the `app/navigation`
|
16
|
+
directory, and rendered in the view with a simple helper method.
|
17
|
+
|
18
|
+
|
19
|
+
# Getting Started
|
20
|
+
|
21
|
+
Include the gem in your `Gemfile` like so:
|
22
|
+
|
23
|
+
gem 'menumatic'
|
24
|
+
|
25
|
+
Then update your bundle:
|
26
|
+
|
27
|
+
$ bundle
|
28
|
+
|
29
|
+
You can then get started by generating a new navigation:
|
30
|
+
|
31
|
+
$ rails g navigation
|
32
|
+
|
33
|
+
This will create the file `app/navigation/application_navigation.rb` which you can use to
|
34
|
+
define the structure of your navigation items.
|
35
|
+
|
36
|
+
If you want to use the bundled stylesheet (nothing fancy, but quite serviceable - enough to get you up and running), add the following to your
|
37
|
+
layout:
|
38
|
+
|
39
|
+
<%= stylesheet_link_tag "application_navigation" %>
|
40
|
+
|
41
|
+
|
42
|
+
# Using Menumatic
|
43
|
+
|
44
|
+
Navigations are stored in `app/navigation`. The default navigation is
|
45
|
+
given a name of 'application', however you can specify the name in the
|
46
|
+
generator:
|
47
|
+
|
48
|
+
$ rails g navigation application
|
49
|
+
|
50
|
+
Which will generate a navigation called
|
51
|
+
`app/navigation/application_navigation.rb` and the basic stylesheet.
|
52
|
+
|
53
|
+
Inside your navigation file, you can then define the structure of your
|
54
|
+
navigation, here is an example taken from the [Menumatic example
|
55
|
+
application](http://github.com/thetron/menumatic-sample-application):
|
56
|
+
|
57
|
+
navigate_to "Home", home_path, :active_on => [:home_path, :root_path]
|
58
|
+
navigate_to "About", about_path
|
59
|
+
navigate_to "Store", store_on_special_path do |store|
|
60
|
+
store.navigate_to "On special", store_on_special_path
|
61
|
+
store.navigate_to "Coming soon", store_coming_soon_path
|
62
|
+
store.navigate_to "Categories", store_categories_path do |categories|
|
63
|
+
categories.navigate_to "Shirts", store_categories_shirts_path
|
64
|
+
categories.navigate_to "Pants", store_categories_pants_path
|
65
|
+
categories.navigate_to "Hats", store_categories_hats_path
|
66
|
+
categories.navigate_to "Accessories", store_categories_accessories_path
|
67
|
+
categories.navigate_to "Sporks", store_categories_sporks_path
|
68
|
+
end
|
69
|
+
end
|
70
|
+
navigate_to "Contact us", contact_us_path
|
71
|
+
navigate_to "Admin", admin_path if current_user.is_admin?
|
72
|
+
|
73
|
+
When creating your navigation you effectively have two methods at your
|
74
|
+
disposal: `navigate_to` and `group`
|
75
|
+
|
76
|
+
`navigate_to` creates a navigation item in your menu, and works very
|
77
|
+
similar to Rails' built-in `link_to` helper. It also accepts a few other
|
78
|
+
options, which are listed below.
|
79
|
+
|
80
|
+
To display your navigation in your view, simply use the menumatic
|
81
|
+
helper:
|
82
|
+
|
83
|
+
# app/views/layouts/application.html.erb
|
84
|
+
<!-- snip -->
|
85
|
+
<header>
|
86
|
+
<nav>
|
87
|
+
<%= navigation :application %>
|
88
|
+
</nav>
|
89
|
+
</header>
|
90
|
+
|
91
|
+
Which will give you a full-semantic, auto-highlighted navigation.
|
92
|
+
|
93
|
+
|
94
|
+
## Rendering specific navigation levels
|
95
|
+
|
96
|
+
By default, Menumatic displays your navigation as a set of nested
|
97
|
+
unordered lists. However, if you need to break up your layout, this is
|
98
|
+
easily achiveable:
|
99
|
+
|
100
|
+
|
101
|
+
# app/views/layouts/application.html.erb
|
102
|
+
<!-- snip -->
|
103
|
+
<header>
|
104
|
+
<nav>
|
105
|
+
<%= navigation :application, :level => :primary %>
|
106
|
+
</nav>
|
107
|
+
</header>
|
108
|
+
|
109
|
+
<div class="sub_navigation">
|
110
|
+
<%= navigation :application, :levels => [:secondary, :tertiary] %>
|
111
|
+
</div>
|
112
|
+
|
113
|
+
The above example would render the top-level navigation in the
|
114
|
+
`<header>` and everything else in the `sub_navigation` div below.
|
115
|
+
|
116
|
+
|
117
|
+
## Sitemap generation
|
118
|
+
|
119
|
+
Menumatic can also optionally generate a sitemap.xml. To include a
|
120
|
+
|
121
|
+
sitemap in your site, simply add this line to your `config/routes.rb`
|
122
|
+
|
123
|
+
sitemap_for :application # or the name of your navigation
|
124
|
+
|
125
|
+
|
126
|
+
# Todo
|
127
|
+
|
128
|
+
I have some big ideas for Menumatic, but there should be enough
|
129
|
+
functionality in there to service the vast majority of web apps. If you have
|
130
|
+
any feature requests or questions, feel free to [open an issue](http://github.com/thetron/menumatic/issues) - I'd love to get
|
131
|
+
some feedback!
|
132
|
+
|
133
|
+
This is what I have planned for some future releases:
|
134
|
+
|
135
|
+
* __Tests__ I know I should've been doing this properly from the start,
|
136
|
+
but i'm still trying to get my head around TDD. However, this is definitely a
|
137
|
+
big priority for Menumatic.
|
138
|
+
|
139
|
+
* __More configuration options__
|
140
|
+
|
141
|
+
|
142
|
+
# Credits
|
143
|
+
|
144
|
+
I'd like to thank [Jordan Lewis](http://github.com/jordan-lewis) for his CSS styling skills.
|
data/Rakefile
ADDED
data/TODO.md
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# Menumatic - TODO
|
2
|
+
|
3
|
+
## Refactor
|
4
|
+
|
5
|
+
`Menumatic::Navigation::Item::Base` needs to become the superclass of the two navigator options:
|
6
|
+
- Menumatic::Navigation::Item::Group
|
7
|
+
- Menumatic::Navigation::Item::Link
|
8
|
+
|
9
|
+
The difference between these two, is that `Link` will have significanly
|
10
|
+
more attributes, and also have included
|
11
|
+
`Menumatic::Navigation::Item::Renderers`. Additionally, the 'root' of
|
12
|
+
any Menumatic::Navigation::Base, will be converted into a `Group` with
|
13
|
+
`id = :root`.
|
14
|
+
|
15
|
+
This will allow us to handle the render chain much more logically, and
|
16
|
+
hopefully produce some cleaner code.
|
17
|
+
|
18
|
+
## Selective Navigation rendering
|
19
|
+
|
20
|
+
This should be the next highest priority, and we need to consider how to
|
21
|
+
pass the requested groups and chain position through the render process,
|
22
|
+
to make sure no link is left behind.
|
23
|
+
|
24
|
+
## Sitemap generation
|
25
|
+
|
26
|
+
Create a helper method for the routes.rb file (similar to `devise_for`):
|
27
|
+
|
28
|
+
sitemap :application
|
29
|
+
|
30
|
+
Which will create a route for sitemap.xml and automatically generate and
|
31
|
+
handle the requests that go through to it. The sitemap will be generated
|
32
|
+
from the navigation specified in the sitemap helper. This will include
|
33
|
+
all links and groups (except those not visible by default from
|
34
|
+
conditional effects).
|
35
|
+
|
36
|
+
|
data/lib/.DS_Store
ADDED
Binary file
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Menumatic
|
2
|
+
module Generators
|
3
|
+
class NavigationGenerator < Rails::Generators::Base
|
4
|
+
desc "Creates a new navigation file"
|
5
|
+
source_root File.expand_path('../templates', __FILE__)
|
6
|
+
argument :navigation_name, :type => :string, :default => "application"
|
7
|
+
class_option :stylesheet, :type => :boolean, :default => true, :description => "Include stylesheet file"
|
8
|
+
|
9
|
+
def generate_navigation
|
10
|
+
template "navigation.rb", "app/navigation/#{file_name}_navigation.rb"
|
11
|
+
end
|
12
|
+
|
13
|
+
def generate_stylesheet
|
14
|
+
template "stylesheet.css", "public/stylesheets/#{file_name}_navigation.css" unless options.skip_stylesheet?
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
def file_name
|
19
|
+
navigation_name.underscore
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
ul.navigation.<%= file_name %>, ul.navigation.<%= file_name %> ul {
|
2
|
+
font-family:Tahoma,Arial,sans-serif; font-size:12px; margin:0px; padding:0px;
|
3
|
+
width: 100%;
|
4
|
+
background: #ccc;
|
5
|
+
display:block;
|
6
|
+
position:absolute;
|
7
|
+
top:30px;
|
8
|
+
left: 0;
|
9
|
+
list-style:none;
|
10
|
+
padding:0;
|
11
|
+
margin: 0;
|
12
|
+
}
|
13
|
+
|
14
|
+
ul.navigation.<%= file_name %>.level_1 > li > a{
|
15
|
+
padding: 8px 10px;
|
16
|
+
}
|
17
|
+
|
18
|
+
ul.navigation.<%= file_name %> li {
|
19
|
+
margin:0;
|
20
|
+
padding:0 10px;
|
21
|
+
list-style:none;
|
22
|
+
float:left;
|
23
|
+
height: 30px;
|
24
|
+
}
|
25
|
+
ul.navigation.<%= file_name %> li:hover,
|
26
|
+
ul.navigation.<%= file_name %> li.active { color:#FFF; text-decoration:none; }
|
27
|
+
ul.navigation.<%= file_name %> li a {
|
28
|
+
padding:0 10px;
|
29
|
+
margin:0;
|
30
|
+
color:#FFF;
|
31
|
+
text-decoration: none;
|
32
|
+
display: block;
|
33
|
+
}
|
34
|
+
|
35
|
+
|
36
|
+
/* Level 1 */
|
37
|
+
ul.navigation.<%= file_name %>.level_1 { position: relative; top: 0; }
|
38
|
+
ul.navigation.<%= file_name %>.level_1 > li.active,
|
39
|
+
ul.navigation.<%= file_name %>.level_1 > li.active:hover { background:#444; }
|
40
|
+
ul.navigation.<%= file_name %>.level_1 > li:hover { background:#689cfe; }
|
41
|
+
|
42
|
+
/* Levels 2+ */
|
43
|
+
ul.navigation.<%= file_name %>.level_1 { background: #3b7df6; }
|
44
|
+
ul.navigation.<%= file_name %> ul.level_2 { background: #444; }
|
45
|
+
ul.navigation.<%= file_name %> ul.level_3 { background: #666; }
|
46
|
+
ul.navigation.<%= file_name %> ul.level_4 { background: #828282; }
|
47
|
+
|
48
|
+
ul.navigation.<%= file_name %> ul li a {
|
49
|
+
height: 20px;
|
50
|
+
margin-top: 5px;
|
51
|
+
line-height: 20px;
|
52
|
+
}
|
53
|
+
ul.navigation.<%= file_name %> ul li a:hover,
|
54
|
+
ul.navigation.<%= file_name %> ul li.active > a {
|
55
|
+
background:#b3b3b3;
|
56
|
+
color:#FFF;
|
57
|
+
text-decoration:none;
|
58
|
+
-webkit-border-radius: 5px;
|
59
|
+
-moz-border-radius: 5px;
|
60
|
+
border-radius: 5px;
|
61
|
+
}
|
62
|
+
|
63
|
+
ul.navigation.<%= file_name %>.depth_1 { height: 30px; }
|
64
|
+
ul.navigation.<%= file_name %>.depth_2 { height: 60px; }
|
65
|
+
ul.navigation.<%= file_name %>.depth_3 { height: 90px; }
|
66
|
+
ul.navigation.<%= file_name %>.depth_4 { height: 120px; }
|
67
|
+
ul.navigation.<%= file_name %>.depth_5 { height: 150px; }
|
68
|
+
ul.navigation.<%= file_name %>.depth_6 { height: 180px; }
|
69
|
+
ul.navigation.<%= file_name %>.depth_7 { height: 210px; }
|
70
|
+
ul.navigation.<%= file_name %>.depth_8 { height: 240px; }
|
71
|
+
ul.navigation.<%= file_name %>.depth_9 { height: 270px; }
|
72
|
+
ul.navigation.<%= file_name %>.depth_10 { height: 300px; }
|
73
|
+
|
Binary file
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Menumatic
|
2
|
+
module Helpers
|
3
|
+
module NavigationHelper
|
4
|
+
def navigation(navigation_id = "application", options = {})
|
5
|
+
options[:level] ||= nil # single level to render, overrides any other level settings
|
6
|
+
options[:levels] ||= [options[:level]].delete_if{ |l| l.blank? }
|
7
|
+
|
8
|
+
options[:group] ||= nil
|
9
|
+
options[:groups] ||= [options[:groups]].delete_if{ |g| g.blank? }
|
10
|
+
|
11
|
+
options[:class] ||= ""
|
12
|
+
options[:class] += "navigation #{navigation_id}"
|
13
|
+
|
14
|
+
navigation = load_navigation(navigation_id)
|
15
|
+
navigation.root.render(request, options)
|
16
|
+
end
|
17
|
+
|
18
|
+
def sitemap(document, navigation_id = "application", options = {})
|
19
|
+
navigation = load_navigation(navigation_id)
|
20
|
+
navigation.root.render_sitemap(document, request, options)
|
21
|
+
end
|
22
|
+
|
23
|
+
def load_navigation(navigation_id)
|
24
|
+
# Eager load the requested navgation (allows for use of normal if/unless statements)
|
25
|
+
Menumatic::Navigation::Base.destroy_all
|
26
|
+
load "app/navigation/#{navigation_id}_navigation.rb"
|
27
|
+
Menumatic::Navigation::Base.get(navigation_id)
|
28
|
+
end
|
29
|
+
|
30
|
+
def navigation_group(navigation_id, group_id, options = {})
|
31
|
+
options = options.merge({:group => group_id})
|
32
|
+
navigation(navigation_id, options)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
Binary file
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Menumatic
|
2
|
+
module Navigation
|
3
|
+
module Item
|
4
|
+
class Link < Menumatic::Navigation::Item::Base
|
5
|
+
attr_accessor :label, :destination, :active_paths
|
6
|
+
def initialize(*args)
|
7
|
+
super
|
8
|
+
self.label = args[0]
|
9
|
+
self.destination = args[1]
|
10
|
+
|
11
|
+
if args.length > 2
|
12
|
+
options = args[2]
|
13
|
+
self.active_paths = options[:active_paths]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Menumatic
|
2
|
+
module Navigation
|
3
|
+
module Item
|
4
|
+
module Navigators
|
5
|
+
def navigate_to(label, destination, options = {})
|
6
|
+
item = add_item(Menumatic::Navigation::Item::Link.new(label, destination, options))
|
7
|
+
yield item if block_given?
|
8
|
+
end
|
9
|
+
|
10
|
+
def group(id, options = {})
|
11
|
+
item = add_item(Menumatic::Navigation::Item::Group.new(id, options))
|
12
|
+
yield item if block_given?
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
module Menumatic
|
2
|
+
module Navigation
|
3
|
+
module Item
|
4
|
+
module Renderers
|
5
|
+
include ActionView::Helpers::UrlHelper
|
6
|
+
@@level_options = [:primary, :secondary, :tertiary, :quarternary, :quinary, :senary, :septenary, :octonary, :nonary, :denary]
|
7
|
+
|
8
|
+
def render(request, options = {})
|
9
|
+
options[:current_level] ||= 0
|
10
|
+
|
11
|
+
html_options = {}
|
12
|
+
html_options[:class] = options[:class]
|
13
|
+
options.delete(:class)
|
14
|
+
options = options.merge({})
|
15
|
+
options[:current_level] += 1
|
16
|
+
|
17
|
+
options[:show] ||= :active
|
18
|
+
options[:wrapper_tag] ||= :ul
|
19
|
+
options[:item_tag] ||= :li
|
20
|
+
|
21
|
+
# render list
|
22
|
+
list = self.items.map { |item| item.render(request, options) }.join("")
|
23
|
+
html_options[:class] ||= ""
|
24
|
+
html_options[:class] = (html_options[:class].split(" ") + (self.is_active?(request) ? ["active"] : ["inactive"])).join(" ")
|
25
|
+
|
26
|
+
unless list.blank?
|
27
|
+
list_options = html_options.merge({})
|
28
|
+
list_options[:class] += " level_#{options[:current_level]}"
|
29
|
+
list_options[:class] += " depth_#{count_active_descendants(request)}" if options[:current_level] == 1
|
30
|
+
if on_valid_level?(options[:levels], options[:current_level]) || options[:current_level] == 1
|
31
|
+
list = content_tag(options[:wrapper_tag], list.html_safe, list_options)
|
32
|
+
else
|
33
|
+
list = list.html_safe
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# render link
|
38
|
+
link = ""
|
39
|
+
link = link_to(self.label, self.destination).html_safe if self.is_link? && !options[:group]
|
40
|
+
|
41
|
+
if on_valid_level?(options[:levels], options[:current_level])
|
42
|
+
if options[:current_level] == 1 || (self.is_group? && options[:group] == self.group_id)
|
43
|
+
list.html_safe
|
44
|
+
elsif options[:show] == :all || self.is_active?(request)
|
45
|
+
content_tag(options[:item_tag], link.to_s + list.to_s, html_options).to_s.html_safe
|
46
|
+
elsif self.is_link?
|
47
|
+
content_tag(options[:item_tag], link, html_options).to_s.html_safe
|
48
|
+
end
|
49
|
+
elsif self.is_active?(request)
|
50
|
+
list.html_safe
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def render_sitemap(document, request, options = {})
|
55
|
+
if is_link?
|
56
|
+
unless self.destination[0...10] == "javascript"
|
57
|
+
document.url do
|
58
|
+
if self.destination[0...4] == "http"
|
59
|
+
document.loc self.destination
|
60
|
+
else
|
61
|
+
document.loc request.protocol + request.host_with_port + self.destination
|
62
|
+
end
|
63
|
+
document.changefreq "weekly"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
self.items.each{ |item| item.render_sitemap(document, request, options) }
|
68
|
+
end
|
69
|
+
|
70
|
+
def is_active?(request)
|
71
|
+
has_active_descendant?(request) || paths_match?(request)
|
72
|
+
end
|
73
|
+
|
74
|
+
def has_active_descendant?(request, include_self = true)
|
75
|
+
self.items.each do |item|
|
76
|
+
return true if item.has_active_descendant?(request)
|
77
|
+
end
|
78
|
+
return true if paths_match?(request) if include_self
|
79
|
+
false
|
80
|
+
end
|
81
|
+
|
82
|
+
def count_active_descendants(request)
|
83
|
+
self.items.each do |item|
|
84
|
+
return item.count_active_descendants(request) + 1 if item.has_active_descendant?(request)
|
85
|
+
end
|
86
|
+
|
87
|
+
if (self.paths_match?(request) && !self.has_active_descendant?(request, false) && self.items.count > 0)
|
88
|
+
return 1
|
89
|
+
elsif (self.is_group? && !self.has_active_descendant?(request)) || (self.paths_match?(request) && !self.has_active_descendant?(request, false))
|
90
|
+
return 0
|
91
|
+
end
|
92
|
+
0
|
93
|
+
end
|
94
|
+
|
95
|
+
def paths_match?(request)
|
96
|
+
request.fullpath == self.destination || request.url == self.destination if self.is_link?
|
97
|
+
end
|
98
|
+
|
99
|
+
def on_valid_level?(levels, current_level)
|
100
|
+
levels_to_i(levels).include?(current_level-1) || levels.empty?
|
101
|
+
end
|
102
|
+
|
103
|
+
def levels_to_i(levels_in_words)
|
104
|
+
levels_in_words.map{ |word| @@level_options.index(word.to_sym) + 1 }
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Menumatic
|
2
|
+
module Navigation
|
3
|
+
module Item
|
4
|
+
class Base
|
5
|
+
attr_accessor :items, :options
|
6
|
+
|
7
|
+
include Menumatic::Navigation::Item::Renderers
|
8
|
+
include Menumatic::Navigation::Item::Navigators
|
9
|
+
|
10
|
+
def initialize(*args)
|
11
|
+
self.options = args.last
|
12
|
+
self.items = []
|
13
|
+
end
|
14
|
+
|
15
|
+
def is_link?
|
16
|
+
self.is_a? Menumatic::Navigation::Item::Link
|
17
|
+
end
|
18
|
+
|
19
|
+
def is_group?
|
20
|
+
self.is_a? Menumatic::Navigation::Item::Group
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
def add_item(item)
|
25
|
+
self.items << item
|
26
|
+
item
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Menumatic
|
2
|
+
module Navigation
|
3
|
+
class Base
|
4
|
+
include ActionView::Helpers
|
5
|
+
@@navigations = {}
|
6
|
+
attr_accessor :id, :root
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def navigate_to(label, destination, options = {})
|
10
|
+
if block_given?
|
11
|
+
item = self.get_instance.root.navigate_to(label, destination, options, &Proc.new)
|
12
|
+
else
|
13
|
+
item = self.get_instance.root.navigate_to(label, destination, options)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def group(id)
|
18
|
+
if block_given?
|
19
|
+
group = self.get_instance.root.group(id, &Proc.new)
|
20
|
+
else
|
21
|
+
group = self.get_instance.root.group(id)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def get_instance
|
26
|
+
id = self.name.split("Navigation").first.underscore.to_sym
|
27
|
+
@@navigations[id] = self.new(id) unless @@navigations.has_key?(id)
|
28
|
+
@@navigations[id]
|
29
|
+
end
|
30
|
+
|
31
|
+
def get(id)
|
32
|
+
unless @@navigations.has_key?(id)
|
33
|
+
Module.const_get("#{id.to_s.camelize}Navigation").get_instance
|
34
|
+
end
|
35
|
+
@@navigations[id]
|
36
|
+
end
|
37
|
+
|
38
|
+
def destroy_all
|
39
|
+
@@navigations = {}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def initialize(id)
|
44
|
+
self.id = id
|
45
|
+
self.root = Menumatic::Navigation::Item::Group.new("#{id}_root".to_sym)
|
46
|
+
end
|
47
|
+
|
48
|
+
def items
|
49
|
+
self.root.items
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
data/lib/menumatic.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
module Menumatic
|
2
|
+
module Helpers
|
3
|
+
autoload :NavigationHelper, 'menumatic/helpers/navigation_helper'
|
4
|
+
ActionController::Base.helper(Menumatic::Helpers::NavigationHelper)
|
5
|
+
end
|
6
|
+
|
7
|
+
module Navigation
|
8
|
+
module Item
|
9
|
+
autoload :Renderers, 'menumatic/navigation/item/renderers'
|
10
|
+
autoload :Navigators, 'menumatic/navigation/item/navigators'
|
11
|
+
autoload :Group, 'menumatic/navigation/item/group'
|
12
|
+
autoload :Link, 'menumatic/navigation/item/link'
|
13
|
+
autoload :Base, 'menumatic/navigation/item'
|
14
|
+
end
|
15
|
+
autoload :Base, 'menumatic/navigation'
|
16
|
+
end
|
17
|
+
|
18
|
+
#autoload :Mapper, 'menumatic/action_dispatch/routing/mapper'
|
19
|
+
#autoload :SitemapController, '../app/controllers/menumatic
|
20
|
+
end
|
21
|
+
|
22
|
+
require 'menumatic/rails/routes'
|
23
|
+
require 'menumatic/engine'
|
data/menumatic.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "menumatic/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "menumatic"
|
7
|
+
s.version = Menumatic::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Nicholas Bruning"]
|
10
|
+
s.email = ["nicholas@bruning.com.au"]
|
11
|
+
s.homepage = ""
|
12
|
+
s.summary = %q{Menumatic is a Rails 3 gem which aims to simplify building complex website navigation}
|
13
|
+
s.description = %q{Menumatic is a Rails 3 exclusive gem which aims to take all the fuss and clutter out of developing semantic, usable navigation menus.}
|
14
|
+
|
15
|
+
s.rubyforge_project = "menumatic"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
end
|
data/spec/factories.rb
ADDED
data/spec/link_spec.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'active_resource/http_mock'
|
3
|
+
|
4
|
+
describe Menumatic::Navigation::Item::Link do
|
5
|
+
|
6
|
+
before :each do
|
7
|
+
@label = "Search"
|
8
|
+
@destination = "/search"
|
9
|
+
@options = {}
|
10
|
+
@link = Menumatic::Navigation::Item::Link.new(@label, @destination, @options)
|
11
|
+
|
12
|
+
ActiveResource::HttpMock.respond_to do |mock|
|
13
|
+
mock.get '/search', {}, ''
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should have a label" do
|
18
|
+
@link.should respond_to(:label)
|
19
|
+
@link.label.should == @label
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should have a destination" do
|
23
|
+
@link.should respond_to(:destination)
|
24
|
+
@link.destination.should == @destination
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should be a link" do
|
28
|
+
@link.is_link?.should == true
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should not be a group" do
|
32
|
+
@link.is_group?.should == false
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should be active when destination is the same as the request path" do
|
36
|
+
request = ActiveResource::Request.new(:get, @link.destination)
|
37
|
+
@link.is_active?(request).should == true
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should not be active when destination is not the same as the request path" do
|
41
|
+
@link.is_active?(request).should == false
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should be able to parent links"
|
45
|
+
it "should be able to parent groups"
|
46
|
+
|
47
|
+
it "should render a list item containing only a link when it has no children"
|
48
|
+
it "should render a list item containing only a link when it has no active descendants"
|
49
|
+
it "should render a list item containing an active link when active"
|
50
|
+
it "should render a list item containing a list of its children when active"
|
51
|
+
it "should render a list item containing an active link and a list of its children when it has an active descendant"
|
52
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: menumatic
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Nicholas Bruning
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-03-08 00:00:00 +11:00
|
19
|
+
default_executable:
|
20
|
+
dependencies: []
|
21
|
+
|
22
|
+
description: Menumatic is a Rails 3 exclusive gem which aims to take all the fuss and clutter out of developing semantic, usable navigation menus.
|
23
|
+
email:
|
24
|
+
- nicholas@bruning.com.au
|
25
|
+
executables: []
|
26
|
+
|
27
|
+
extensions: []
|
28
|
+
|
29
|
+
extra_rdoc_files: []
|
30
|
+
|
31
|
+
files:
|
32
|
+
- .DS_Store
|
33
|
+
- .autotest
|
34
|
+
- .gitignore
|
35
|
+
- .rspec
|
36
|
+
- Gemfile
|
37
|
+
- Gemfile.lock
|
38
|
+
- LICENSE
|
39
|
+
- README.md
|
40
|
+
- Rakefile
|
41
|
+
- TODO.md
|
42
|
+
- app/controllers/menumatic/sitemap_controller.rb
|
43
|
+
- app/views/menumatic/sitemap/index.xml.builder
|
44
|
+
- lib/.DS_Store
|
45
|
+
- lib/generators/menumatic/USAGE
|
46
|
+
- lib/generators/menumatic/navigation_generator.rb
|
47
|
+
- lib/generators/menumatic/templates/navigation.rb
|
48
|
+
- lib/generators/menumatic/templates/stylesheet.css
|
49
|
+
- lib/menumatic.rb
|
50
|
+
- lib/menumatic/.DS_Store
|
51
|
+
- lib/menumatic/engine.rb
|
52
|
+
- lib/menumatic/helpers/navigation_helper.rb
|
53
|
+
- lib/menumatic/navigation.rb
|
54
|
+
- lib/menumatic/navigation/.DS_Store
|
55
|
+
- lib/menumatic/navigation/item.rb
|
56
|
+
- lib/menumatic/navigation/item/group.rb
|
57
|
+
- lib/menumatic/navigation/item/link.rb
|
58
|
+
- lib/menumatic/navigation/item/navigators.rb
|
59
|
+
- lib/menumatic/navigation/item/renderers.rb
|
60
|
+
- lib/menumatic/rails/routes.rb
|
61
|
+
- lib/menumatic/version.rb
|
62
|
+
- menumatic.gemspec
|
63
|
+
- spec/factories.rb
|
64
|
+
- spec/link_spec.rb
|
65
|
+
- spec/spec_helper.rb
|
66
|
+
has_rdoc: true
|
67
|
+
homepage: ""
|
68
|
+
licenses: []
|
69
|
+
|
70
|
+
post_install_message:
|
71
|
+
rdoc_options: []
|
72
|
+
|
73
|
+
require_paths:
|
74
|
+
- lib
|
75
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
76
|
+
none: false
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
hash: 3
|
81
|
+
segments:
|
82
|
+
- 0
|
83
|
+
version: "0"
|
84
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
85
|
+
none: false
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
hash: 3
|
90
|
+
segments:
|
91
|
+
- 0
|
92
|
+
version: "0"
|
93
|
+
requirements: []
|
94
|
+
|
95
|
+
rubyforge_project: menumatic
|
96
|
+
rubygems_version: 1.3.7
|
97
|
+
signing_key:
|
98
|
+
specification_version: 3
|
99
|
+
summary: Menumatic is a Rails 3 gem which aims to simplify building complex website navigation
|
100
|
+
test_files:
|
101
|
+
- spec/factories.rb
|
102
|
+
- spec/link_spec.rb
|
103
|
+
- spec/spec_helper.rb
|