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.
@@ -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