navigatrix 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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 "action_view"
2
-
3
- module Navigatrix::Rendering::Strategies
4
- class List < Struct.new(:items, :options)
5
- include ActionView::Helpers::TagHelper
6
- include ActionView::Helpers::UrlHelper
7
-
8
- def render
9
- content_tag(:ul, render_items, html_attributes)
10
- end
11
-
12
- def render_items
13
- items.map(&:render).join.html_safe
14
- end
15
-
16
- private
17
-
18
- def items
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
- def html_attributes
72
- HTMLAttributes.new(super).merge_attribute(:class, html_class)
73
- end
20
+ def render
21
+ content_tag(:ul, render_items, html_attributes)
22
+ end
74
23
 
75
- def html_class
76
- active? ? active_class : inactive_class
77
- end
24
+ private
78
25
 
79
- def active_class
80
- options[:active_class] || "active"
81
- end
26
+ def render_items
27
+ items.map(&:render).join.html_safe
28
+ end
82
29
 
83
- def inactive_class
84
- options[:inactive_class]
85
- end
86
- end
30
+ def items
31
+ @items.map { |item| item_class.new(item, item_options) }
32
+ end
87
33
 
88
- class HTMLAttributes < HashWithIndifferentAccess
89
- def merge_attribute(attribute, value)
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
- def attribute_values(attribute)
96
- fetch(attribute, "").to_s.split(" ")
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
@@ -1,3 +1,3 @@
1
1
  module Navigatrix
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -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