navigatrix 0.0.1 → 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.
- checksums.yaml +14 -6
- data/README.md +108 -7
- data/lib/navigatrix.rb +23 -0
- data/lib/navigatrix/builder.rb +69 -0
- data/lib/navigatrix/configuration.rb +10 -0
- data/lib/navigatrix/item.rb +1 -1
- data/lib/navigatrix/renderer.rb +20 -14
- data/lib/navigatrix/rendering/strategies/bootstrap/navbar.rb +27 -23
- data/lib/navigatrix/rendering/strategies/bootstrap/tabs.rb +10 -6
- data/lib/navigatrix/rendering/strategies/content_helpers.rb +16 -0
- data/lib/navigatrix/rendering/strategies/html_attributes.rb +27 -0
- data/lib/navigatrix/rendering/strategies/item.rb +102 -0
- data/lib/navigatrix/rendering/strategies/list.rb +33 -89
- data/lib/navigatrix/rendering/strategy_factory.rb +28 -0
- data/lib/navigatrix/version.rb +1 -1
- data/spec/integration/documentation_spec.rb +154 -0
- data/spec/item_builder_spec.rb +120 -0
- data/spec/item_spec.rb +1 -3
- data/spec/list_builder_spec.rb +32 -0
- data/spec/renderer_spec.rb +23 -20
- data/spec/{strategies → rendering/strategies}/bootstrap/navbar_spec.rb +2 -4
- data/spec/{strategies → rendering/strategies}/bootstrap/tabs_spec.rb +1 -3
- data/spec/rendering/strategies/list_spec.rb +13 -0
- data/spec/rendering/strategy_factory_spec.rb +58 -0
- data/spec/spec_helper.rb +5 -0
- data/spec/support/list_rendering_strategy.rb +11 -5
- metadata +37 -21
- data/spec/strategies/list_spec.rb +0 -11
@@ -0,0 +1,16 @@
|
|
1
|
+
require "action_view"
|
2
|
+
|
3
|
+
module Navigatrix
|
4
|
+
module Rendering
|
5
|
+
module Strategies
|
6
|
+
module ContentHelpers
|
7
|
+
include ActionView::Helpers::TagHelper
|
8
|
+
include ActionView::Helpers::UrlHelper
|
9
|
+
|
10
|
+
def capture
|
11
|
+
yield
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require "active_support/core_ext"
|
2
|
+
|
3
|
+
module Navigatrix
|
4
|
+
module Rendering
|
5
|
+
module Strategies
|
6
|
+
class HTMLAttributes < HashWithIndifferentAccess
|
7
|
+
def merge(other)
|
8
|
+
other.inject(self) do |this, (attribute, value)|
|
9
|
+
this.merge_attribute(attribute, value)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def merge_attribute(attribute, value)
|
14
|
+
return self unless value.present?
|
15
|
+
|
16
|
+
tap do
|
17
|
+
self[attribute] = attribute_values(attribute).push(value).join(" ")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def attribute_values(attribute)
|
22
|
+
fetch(attribute, "").to_s.split(" ")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require "delegate"
|
2
|
+
|
3
|
+
require "navigatrix/rendering/strategy_factory"
|
4
|
+
require "navigatrix/rendering/strategies/html_attributes"
|
5
|
+
require "navigatrix/rendering/strategies/content_helpers"
|
6
|
+
|
7
|
+
module Navigatrix
|
8
|
+
module Rendering
|
9
|
+
module Strategies
|
10
|
+
class Item < SimpleDelegator
|
11
|
+
include ContentHelpers
|
12
|
+
|
13
|
+
attr_reader :options
|
14
|
+
|
15
|
+
def initialize(item, options = {})
|
16
|
+
super(item)
|
17
|
+
@options = options || {}
|
18
|
+
end
|
19
|
+
|
20
|
+
def render
|
21
|
+
content_tag(:li, content, html_attributes) if render?
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def content
|
27
|
+
name_or_link + render_children
|
28
|
+
end
|
29
|
+
|
30
|
+
def name_or_link
|
31
|
+
linked? ? linked_content : unlinked_content
|
32
|
+
end
|
33
|
+
|
34
|
+
def linked_content
|
35
|
+
link_to(name, path)
|
36
|
+
end
|
37
|
+
|
38
|
+
def unlinked_content
|
39
|
+
name
|
40
|
+
end
|
41
|
+
|
42
|
+
def name
|
43
|
+
super.html_safe
|
44
|
+
end
|
45
|
+
|
46
|
+
def html_attributes
|
47
|
+
universal_attributes
|
48
|
+
.merge(super)
|
49
|
+
.merge_attribute(:class, html_class)
|
50
|
+
end
|
51
|
+
|
52
|
+
def universal_attributes
|
53
|
+
HTMLAttributes.new(options[:html_attributes])
|
54
|
+
end
|
55
|
+
|
56
|
+
def render_children
|
57
|
+
if has_children?
|
58
|
+
children_renderer.new(
|
59
|
+
children,
|
60
|
+
children_list_options,
|
61
|
+
children_item_options
|
62
|
+
).render
|
63
|
+
else
|
64
|
+
""
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def children_options
|
69
|
+
options
|
70
|
+
end
|
71
|
+
|
72
|
+
def html_class
|
73
|
+
active? ? active_class : inactive_class
|
74
|
+
end
|
75
|
+
|
76
|
+
def active_class
|
77
|
+
super.presence || options[:active_class] || "active"
|
78
|
+
end
|
79
|
+
|
80
|
+
def inactive_class
|
81
|
+
super.presence || options[:inactive_class]
|
82
|
+
end
|
83
|
+
|
84
|
+
def children_renderer
|
85
|
+
StrategyFactory.find_list_strategy(strategy_name)
|
86
|
+
end
|
87
|
+
|
88
|
+
def strategy_name
|
89
|
+
children_list_options[:renderer]
|
90
|
+
end
|
91
|
+
|
92
|
+
def children_list_options
|
93
|
+
children_options.fetch(:list, {})
|
94
|
+
end
|
95
|
+
|
96
|
+
def children_item_options
|
97
|
+
children_options.fetch(:item, {})
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -1,99 +1,43 @@
|
|
1
|
-
require "
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
super.map { |item| item_class.new(item, options) }
|
20
|
-
end
|
21
|
-
|
22
|
-
def item_class
|
23
|
-
Item
|
24
|
-
end
|
25
|
-
|
26
|
-
def html_attributes
|
27
|
-
HTMLAttributes.new(options[:html_attributes])
|
28
|
-
end
|
29
|
-
|
30
|
-
class Item < SimpleDelegator
|
31
|
-
include ActionView::Helpers::TagHelper
|
32
|
-
include ActionView::Helpers::UrlHelper
|
33
|
-
|
34
|
-
attr_reader :options
|
35
|
-
|
36
|
-
def initialize(item, options = {})
|
37
|
-
super(item)
|
38
|
-
@options = options
|
39
|
-
end
|
40
|
-
|
41
|
-
def render
|
42
|
-
content_tag(:li, content, html_attributes) if render?
|
43
|
-
end
|
44
|
-
|
45
|
-
private
|
46
|
-
|
47
|
-
def content
|
48
|
-
name_or_link + nested_list.to_s
|
49
|
-
end
|
50
|
-
|
51
|
-
def name_or_link
|
52
|
-
linked? ? link : unlinked_content
|
53
|
-
end
|
54
|
-
|
55
|
-
def nested_list
|
56
|
-
List.new(children, options).render if has_children?
|
57
|
-
end
|
58
|
-
|
59
|
-
def link
|
60
|
-
link_to(name, path)
|
61
|
-
end
|
62
|
-
|
63
|
-
def unlinked_content
|
64
|
-
name
|
65
|
-
end
|
66
|
-
|
67
|
-
def name
|
68
|
-
super.html_safe
|
69
|
-
end
|
1
|
+
require "navigatrix/rendering/strategy_factory"
|
2
|
+
require "navigatrix/rendering/strategies/content_helpers"
|
3
|
+
require "navigatrix/rendering/strategies/html_attributes"
|
4
|
+
require "navigatrix/rendering/strategies/item"
|
5
|
+
|
6
|
+
module Navigatrix
|
7
|
+
module Rendering
|
8
|
+
module Strategies
|
9
|
+
class List
|
10
|
+
include ContentHelpers
|
11
|
+
|
12
|
+
attr_reader :options, :item_options
|
13
|
+
|
14
|
+
def initialize(items, list_options = {}, item_options = {})
|
15
|
+
@items = items || []
|
16
|
+
@options = list_options
|
17
|
+
@item_options = item_options
|
18
|
+
end
|
70
19
|
|
71
|
-
|
72
|
-
|
73
|
-
|
20
|
+
def render
|
21
|
+
content_tag(:ul, render_items, html_attributes)
|
22
|
+
end
|
74
23
|
|
75
|
-
|
76
|
-
active? ? active_class : inactive_class
|
77
|
-
end
|
24
|
+
private
|
78
25
|
|
79
|
-
|
80
|
-
|
81
|
-
|
26
|
+
def render_items
|
27
|
+
items.map(&:render).join.html_safe
|
28
|
+
end
|
82
29
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
end
|
30
|
+
def items
|
31
|
+
@items.map { |item| item_class.new(item, item_options) }
|
32
|
+
end
|
87
33
|
|
88
|
-
|
89
|
-
|
90
|
-
tap do
|
91
|
-
self[attribute] = attribute_values(attribute).push(value).join(" ")
|
34
|
+
def item_class
|
35
|
+
StrategyFactory.find_item_strategy(item_options[:renderer])
|
92
36
|
end
|
93
|
-
end
|
94
37
|
|
95
|
-
|
96
|
-
|
38
|
+
def html_attributes
|
39
|
+
HTMLAttributes.new(options[:html_attributes])
|
40
|
+
end
|
97
41
|
end
|
98
42
|
end
|
99
43
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Navigatrix
|
2
|
+
module Rendering
|
3
|
+
class StrategyFactory
|
4
|
+
DEFAULT_LIST_STRATEGY = :unordered_list
|
5
|
+
DEFAULT_ITEM_STRATEGY = :item
|
6
|
+
|
7
|
+
class MissingStrategy < NameError ; end
|
8
|
+
|
9
|
+
def self.find_list_strategy(strategy_or_name, strategies = Navigatrix.list_renderers)
|
10
|
+
find_strategy(strategy_or_name, strategies, DEFAULT_LIST_STRATEGY)
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.find_item_strategy(strategy_or_name, strategies = Navigatrix.item_renderers)
|
14
|
+
find_strategy(strategy_or_name, strategies, DEFAULT_ITEM_STRATEGY)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.find_strategy(strategy_or_name, strategies, default)
|
18
|
+
return strategy_or_name if strategy_or_name.is_a?(Class)
|
19
|
+
|
20
|
+
strategy_or_name ||= default
|
21
|
+
|
22
|
+
strategies.fetch(strategy_or_name) do
|
23
|
+
raise(MissingStrategy, "can't find strategy #{strategy_or_name}")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/navigatrix/version.rb
CHANGED
@@ -0,0 +1,154 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "documentation" do
|
4
|
+
let(:request) do
|
5
|
+
double({
|
6
|
+
env: {}
|
7
|
+
})
|
8
|
+
end
|
9
|
+
|
10
|
+
let(:render_context) do
|
11
|
+
double({
|
12
|
+
request: request
|
13
|
+
})
|
14
|
+
end
|
15
|
+
|
16
|
+
def render_navigation(configuration, options = {})
|
17
|
+
Navigatrix::Renderer.new(configuration, options.merge(render_context: render_context)).render
|
18
|
+
end
|
19
|
+
|
20
|
+
it "renders a basic navigation" do
|
21
|
+
request.stub(env: {
|
22
|
+
"PATH_INFO" => "/about-us"
|
23
|
+
})
|
24
|
+
rendered = render_navigation({
|
25
|
+
"Home" => "/",
|
26
|
+
"About Us" => "/about-us",
|
27
|
+
"Blog" => "/blog",
|
28
|
+
"Contact" => "/contact"
|
29
|
+
})
|
30
|
+
|
31
|
+
desired = <<-HTML
|
32
|
+
<ul>
|
33
|
+
<li><a href="/">Home</a></li>
|
34
|
+
<li class="active">About Us</li>
|
35
|
+
<li><a href="/blog">Blog</a></li>
|
36
|
+
<li><a href="/contact">Contact</a></li>
|
37
|
+
</ul>
|
38
|
+
HTML
|
39
|
+
|
40
|
+
rendered.should match_html(desired)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "renders a more sophisticated nav" do
|
44
|
+
render_context.stub(controller_name: "users")
|
45
|
+
request.stub(env: {
|
46
|
+
"PATH_INFO" => "/users/1"
|
47
|
+
})
|
48
|
+
|
49
|
+
rendered = render_navigation({
|
50
|
+
"Home" => "/",
|
51
|
+
"Users" => {
|
52
|
+
:path => "/users",
|
53
|
+
:active_states => [
|
54
|
+
{:path => "/my_account"},
|
55
|
+
{:path => /\/users\/\d*/},
|
56
|
+
]
|
57
|
+
},
|
58
|
+
"Sign In" => {
|
59
|
+
:path => "/sign_in",
|
60
|
+
:render? => false
|
61
|
+
},
|
62
|
+
"Sign Out" => {
|
63
|
+
:path => "/sign_out",
|
64
|
+
:render? => true
|
65
|
+
}
|
66
|
+
}, {
|
67
|
+
:item => {
|
68
|
+
:active_class => "active-nav-item"
|
69
|
+
},
|
70
|
+
:list => {
|
71
|
+
:html_attributes => {
|
72
|
+
:class => "nav"
|
73
|
+
}
|
74
|
+
}
|
75
|
+
})
|
76
|
+
|
77
|
+
desired = <<-HTML
|
78
|
+
<ul class="nav">
|
79
|
+
<li><a href="/">Home</a></li>
|
80
|
+
<li class="active-nav-item"><a href="/users">Users</a></li>
|
81
|
+
<li><a href="/sign_out">Sign Out</a></li>
|
82
|
+
</ul>
|
83
|
+
HTML
|
84
|
+
|
85
|
+
rendered.should match_html(desired)
|
86
|
+
end
|
87
|
+
|
88
|
+
it "accepts item options" do
|
89
|
+
render_context.stub(controller_name: "users")
|
90
|
+
request.stub(env: {
|
91
|
+
"PATH_INFO" => "/users"
|
92
|
+
})
|
93
|
+
|
94
|
+
rendered = render_navigation({
|
95
|
+
"Users" => "/users",
|
96
|
+
"Clients" => "/clients"
|
97
|
+
}, {
|
98
|
+
item: {
|
99
|
+
:active_class => "custom-active",
|
100
|
+
:inactive_class => "custom-inactive",
|
101
|
+
:html_attributes => {
|
102
|
+
"class" => "nav-item",
|
103
|
+
"data-item" => "nav"
|
104
|
+
}
|
105
|
+
}
|
106
|
+
})
|
107
|
+
|
108
|
+
rendered.should match_html <<-HTML
|
109
|
+
<ul>
|
110
|
+
<li class="nav-item custom-active" data-item="nav">Users</li>
|
111
|
+
<li class="nav-item custom-inactive" data-item="nav"><a href="/clients">Clients</a></li>
|
112
|
+
</ul>
|
113
|
+
HTML
|
114
|
+
end
|
115
|
+
|
116
|
+
it "accepts item options on each item" do
|
117
|
+
render_context.stub(controller_name: "users")
|
118
|
+
request.stub(env: {
|
119
|
+
"PATH_INFO" => "/users"
|
120
|
+
})
|
121
|
+
|
122
|
+
rendered = render_navigation({
|
123
|
+
"Users" => {
|
124
|
+
:path => "/users",
|
125
|
+
:active_class => "custom-active"
|
126
|
+
},
|
127
|
+
"Clients" => {
|
128
|
+
:path => "/clients",
|
129
|
+
:inactive_class => "custom-inactive"
|
130
|
+
}
|
131
|
+
})
|
132
|
+
|
133
|
+
rendered.should match_html <<-HTML
|
134
|
+
<ul>
|
135
|
+
<li class="custom-active">Users</li>
|
136
|
+
<li class="custom-inactive"><a href="/clients">Clients</a></li>
|
137
|
+
</ul>
|
138
|
+
HTML
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
RSpec::Matchers.define :match_html do |expected|
|
143
|
+
match do |actual|
|
144
|
+
clean(expected) == clean(actual)
|
145
|
+
end
|
146
|
+
|
147
|
+
failure_message_for_should do |actual|
|
148
|
+
"expected that #{clean(actual)} to equal #{clean(expected)}"
|
149
|
+
end
|
150
|
+
|
151
|
+
def clean(text)
|
152
|
+
text.strip.gsub("\n", "").gsub(/(?<=\>)\s+?(?=\<)/, "")
|
153
|
+
end
|
154
|
+
end
|