navigation_builder 1.0.0.beta1 → 1.0.0.beta2
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/README.md +108 -0
- data/VERSION +1 -1
- data/lib/action_view/helpers/navigation_builder.rb +45 -13
- data/navigation_builder.gemspec +1 -1
- data/test/test_navigation_builder.rb +21 -1
- metadata +3 -3
data/README.md
CHANGED
@@ -1,7 +1,115 @@
|
|
1
1
|
Navigation Builder
|
2
2
|
==================
|
3
3
|
|
4
|
+
While at [RailsConf](http://www.railsconf.com) in the Spring/Summer of 2011, [Bruce Williams](http://github.com/bruce) mentioned a gem he wrote called ["Goose"](http://github.com/bruce/goose) that made generating navigation really easy. After looking through the code, I immediately thought of some tweaks that I wanted to make.
|
4
5
|
|
6
|
+
Rather than dealing with forks and pull requests, I just decided to spin off my own implementation from scratch, modeling the API after ActionView's FormBuilder.
|
7
|
+
|
8
|
+
#### That's great, let me see some code. ####
|
9
|
+
|
10
|
+
<% navigation_for :main do |nav| %>
|
11
|
+
<%= nav.link_to 'Home', '#' %>
|
12
|
+
<% end %>
|
13
|
+
|
14
|
+
Generates:
|
15
|
+
|
16
|
+
<ul>
|
17
|
+
<li>
|
18
|
+
<a href="#">Home</a>
|
19
|
+
</li>
|
20
|
+
</ul>
|
21
|
+
|
22
|
+
#### How do you mark a link as selected? ####
|
23
|
+
|
24
|
+
<% navigation_select 'Home' %>
|
25
|
+
|
26
|
+
Make sure that is called *before* the navigation is rendered in the view.
|
27
|
+
|
28
|
+
That will generate:
|
29
|
+
|
30
|
+
<ul>
|
31
|
+
<li class="selected">
|
32
|
+
<a href="#">Home</a>
|
33
|
+
</li>
|
34
|
+
</ul>
|
35
|
+
|
36
|
+
#### I don't want to use the class "selected". I'd rather use "current-page" ####
|
37
|
+
|
38
|
+
<% navigation_for :main, :selected_class => "current-page" do |nav| %>
|
39
|
+
<%= nav.link_to 'Home', '#' %>
|
40
|
+
<% end %>
|
41
|
+
|
42
|
+
#### But my links need additional HTML! ####
|
43
|
+
|
44
|
+
NavigationBuilder supports the same options that the `link_to` helper does:
|
45
|
+
|
46
|
+
<% navigation_for :sub_nav do |nav| %>
|
47
|
+
<% nav.link_to '#' do %>
|
48
|
+
<em>Home</em>
|
49
|
+
<span>Go to your home! Are you too good for your home?!</span>
|
50
|
+
<% end %>
|
51
|
+
<% end %>
|
52
|
+
|
53
|
+
#### Doesn't that mean that I need to repeat the entire `link_to` block when marking a link as selected? ####
|
54
|
+
|
55
|
+
Not at all, just use a regular expression:
|
56
|
+
|
57
|
+
<% navigation_select /Home/ %>
|
58
|
+
|
59
|
+
#### But what if I have multiple blocks of navigation on the page? ####
|
60
|
+
|
61
|
+
<% navigation_select 'Home', :in => :sub_nav %>
|
62
|
+
|
63
|
+
And in your layout:
|
64
|
+
|
65
|
+
<% navigation_for :sub_nav do |nav| %>
|
66
|
+
<%= nav.link_to 'Home', '#' %>
|
67
|
+
<% end %>
|
68
|
+
|
69
|
+
#### Well... what if I need an Ordered List! ####
|
70
|
+
|
71
|
+
<% navigation_for :main, :wrapper_tag => :ol do |nav| %>
|
72
|
+
<%= nav.link_to 'Home', '#' %>
|
73
|
+
<% end %>
|
74
|
+
|
75
|
+
Generates:
|
76
|
+
|
77
|
+
<ol>
|
78
|
+
<li class="selected">
|
79
|
+
<a href="#">Home</a>
|
80
|
+
</li>
|
81
|
+
</ol>
|
82
|
+
|
83
|
+
#### Nevermind, I hate lists. I just want DIVs. ####
|
84
|
+
|
85
|
+
<% navigation_for :main, :nav_item_tag => :div do |nav| %>
|
86
|
+
<%= nav.link_to 'Home', '#' %>
|
87
|
+
<% end %>
|
88
|
+
|
89
|
+
Generates:
|
90
|
+
|
91
|
+
<div class="selected">
|
92
|
+
<a href="#">Home</a>
|
93
|
+
</div>
|
94
|
+
|
95
|
+
#### My designer just said that's DIV-soup. ####
|
96
|
+
|
97
|
+
<% navigation_for :main, :nav_item_tag => false do |nav| %>
|
98
|
+
<%= nav.link_to 'Home', '#' %>
|
99
|
+
<% end %>
|
100
|
+
|
101
|
+
Generates:
|
102
|
+
|
103
|
+
<a href="#" class="selected">Home</a>
|
104
|
+
|
105
|
+
#### Why didn't you name this Gem "Wolfman" ####
|
106
|
+
|
107
|
+
Originally I was going to. But if you came across a method named "wolfman" in your view code, it would not be entirely clear as to what it was going to do.
|
108
|
+
So I just went with something boring and generic.
|
109
|
+
|
110
|
+
#### Well, you've thought of everything, haven't you! ####
|
111
|
+
|
112
|
+
Probably not, let me know if you have any feature requests!
|
5
113
|
|
6
114
|
TODOs
|
7
115
|
=====
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.0.0.
|
1
|
+
1.0.0.beta2
|
@@ -3,15 +3,19 @@ module ActionView
|
|
3
3
|
|
4
4
|
module NavigationBuilderHelper
|
5
5
|
|
6
|
+
# Holds references to the currently selected links for each navigation block on the page.
|
6
7
|
def navigation_builder
|
7
8
|
@navigation_builder ||= { :main => nil }
|
8
9
|
end
|
9
10
|
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
11
|
+
# Generates a block of navigation to be rendered to the page.
|
12
|
+
#
|
13
|
+
# Example:
|
14
|
+
# <% navigation_for :popup do |n| %>
|
15
|
+
# <%= nav_to 'Members', members_path %>
|
16
|
+
# <% end %>
|
17
|
+
#
|
18
|
+
# Generally speaking, this should go in your layout.
|
15
19
|
def navigation_for( nav_name, options = {}, &block )
|
16
20
|
raise ArgumentError, "Missing block" unless block_given?
|
17
21
|
|
@@ -27,11 +31,24 @@ module ActionView
|
|
27
31
|
concat( tag(options[:wrapper_tag], options[:html], true) ) if navigation_has_wrapper?( options )
|
28
32
|
yield builder.new(self, nav_name, options, block)
|
29
33
|
concat("</#{options[:wrapper_tag]}>".html_safe) if navigation_has_wrapper?( options )
|
34
|
+
|
35
|
+
# Mark the navigation block has having been rendered
|
36
|
+
navigation_builder[nav_name] = true
|
30
37
|
end
|
31
38
|
|
39
|
+
# Make sure this is called *before* your navigation is rendered.
|
40
|
+
# Ideally, this should go in your views and navigation_for() should
|
41
|
+
# be in your layout.
|
42
|
+
#
|
43
|
+
# Example:
|
44
|
+
# <%= navigation_select 'Members', :in => :popup %>
|
32
45
|
def navigation_select( link_name, options = {} )
|
33
|
-
options
|
34
|
-
|
46
|
+
if navigation_builder[options[:in]] == true
|
47
|
+
raise RuntimeError, "The #{options[:in]} navigation block has already been rendered. You cannot call navigation_select if navigation_for has already been called."
|
48
|
+
else
|
49
|
+
options.reverse_merge!( :in => :main )
|
50
|
+
navigation_builder[options[:in]] = link_name
|
51
|
+
end
|
35
52
|
end
|
36
53
|
|
37
54
|
private
|
@@ -44,21 +61,28 @@ module ActionView
|
|
44
61
|
|
45
62
|
class NavigationBuilder
|
46
63
|
|
64
|
+
# Initializes the NavigationBuilder.
|
65
|
+
# You'll mostly likely never call this method directly.
|
47
66
|
def initialize( template, nav_name, options, proc )
|
48
67
|
@template, @nav_name, @options, @proc = template, nav_name, options, proc
|
49
68
|
end
|
50
69
|
|
70
|
+
# Builds a link_to tag within the context of the current navigation block.
|
71
|
+
# This accepts all of the same parameters that ActiveView's link_to method
|
72
|
+
#
|
73
|
+
# Example:
|
74
|
+
# <%= nav.link_to 'Home', '#' %>
|
51
75
|
def link_to( *args, &link_block )
|
52
76
|
if block_given?
|
53
77
|
name = @template.capture(&link_block)
|
54
78
|
options = args.first || {}
|
55
|
-
html_options = args.second
|
79
|
+
html_options = args.second || {}
|
56
80
|
|
57
81
|
link_to_in_block( name, options, html_options, &link_block )
|
58
82
|
else
|
59
83
|
name = args[0]
|
60
84
|
options = args[1] || {}
|
61
|
-
html_options = args[2]
|
85
|
+
html_options = args[2] || {}
|
62
86
|
|
63
87
|
link_to_in_html( name, options, html_options )
|
64
88
|
end
|
@@ -69,7 +93,7 @@ module ActionView
|
|
69
93
|
def link_to_in_block( name, options, html_options, &link_block )
|
70
94
|
item_html_options = extract_item_options!( html_options )
|
71
95
|
|
72
|
-
set_selected_link( name, item_html_options ) if is_selected?( name )
|
96
|
+
set_selected_link( name, html_options, item_html_options ) if is_selected?( name )
|
73
97
|
|
74
98
|
@template.concat( @template.tag( @options[:nav_item_tag], item_html_options, true) ) if @options[:nav_item_tag]
|
75
99
|
@template.link_to(options, html_options, &link_block)
|
@@ -79,7 +103,7 @@ module ActionView
|
|
79
103
|
def link_to_in_html( name, options, html_options )
|
80
104
|
item_html_options = extract_item_options!( html_options )
|
81
105
|
|
82
|
-
set_selected_link( name, item_html_options ) if is_selected?( name )
|
106
|
+
set_selected_link( name, html_options, item_html_options ) if is_selected?( name )
|
83
107
|
|
84
108
|
link_html = @template.link_to(name, options, html_options)
|
85
109
|
|
@@ -94,8 +118,16 @@ module ActionView
|
|
94
118
|
options.try(:delete, :item_html) || {}
|
95
119
|
end
|
96
120
|
|
97
|
-
def set_selected_link( name, item_html_options )
|
98
|
-
((item_html_options
|
121
|
+
def set_selected_link( name, html_options, item_html_options )
|
122
|
+
(selected_tag_options( html_options, item_html_options ) << " #{@options[:selected_class]}").strip!
|
123
|
+
end
|
124
|
+
|
125
|
+
def selected_tag_options( html_options, item_html_options )
|
126
|
+
if @options[:nav_item_tag]
|
127
|
+
item_html_options[:class] ||= ''
|
128
|
+
else
|
129
|
+
html_options[:class] ||= ''
|
130
|
+
end
|
99
131
|
end
|
100
132
|
|
101
133
|
def is_selected?( name )
|
data/navigation_builder.gemspec
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{navigation_builder}
|
8
|
-
s.version = "1.0.0.
|
8
|
+
s.version = "1.0.0.beta2"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Derek Lindahl"]
|
@@ -187,8 +187,19 @@ class TestNavigationBuilder < ActionView::TestCase
|
|
187
187
|
assert_dom_equal expected, output_buffer
|
188
188
|
end
|
189
189
|
|
190
|
+
should "generate HTML that highlights the currently selected navigation link even where there is no item tag" do
|
191
|
+
navigation_select 'Foo', :in => :main
|
192
|
+
|
193
|
+
navigation_for :main, :nav_item_tag => false do |nav|
|
194
|
+
concat nav.link_to( 'Foo', '#' )
|
195
|
+
end
|
190
196
|
|
191
|
-
|
197
|
+
expected = [
|
198
|
+
"<a href=\"#\" class=\"selected\">Foo</a>"
|
199
|
+
].join('')
|
200
|
+
|
201
|
+
assert_dom_equal expected, output_buffer
|
202
|
+
end
|
192
203
|
|
193
204
|
should "generate HTML that highlights the currently selected navigation link by using a Regular Expression" do
|
194
205
|
navigation_select /Foo/, :in => :main
|
@@ -211,4 +222,13 @@ class TestNavigationBuilder < ActionView::TestCase
|
|
211
222
|
assert_dom_equal expected, output_buffer
|
212
223
|
end
|
213
224
|
|
225
|
+
should "raise an exception if a link is selected AFTER the navigation has been rendered" do
|
226
|
+
assert_raises RuntimeError do
|
227
|
+
navigation_for :main do |nav|
|
228
|
+
concat nav.link_to( 'Foo', '#' )
|
229
|
+
end
|
230
|
+
navigation_select 'Foo', :in => :main
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
214
234
|
end
|
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: navigation_builder
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 62196359
|
5
5
|
prerelease: 6
|
6
6
|
segments:
|
7
7
|
- 1
|
8
8
|
- 0
|
9
9
|
- 0
|
10
10
|
- beta
|
11
|
-
-
|
12
|
-
version: 1.0.0.
|
11
|
+
- 2
|
12
|
+
version: 1.0.0.beta2
|
13
13
|
platform: ruby
|
14
14
|
authors:
|
15
15
|
- Derek Lindahl
|