navigatrix 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 28321e32d4430f1d16823c1a3d23121d25a1d2a3
4
+ data.tar.gz: 8c466c5479a7330322bb2989a49d11621e13f81a
5
+ SHA512:
6
+ metadata.gz: d7c652b8c6d1e5ddbda0e997b1829307828d2951ae775a680c3407d1c2ff040e2fa444375a0e2facfddfa43d83a6092aa5d993b77a01af0007f758559accbebf
7
+ data.tar.gz: 0eeb7a60fc49e5f7ce8a2e8c683bda0d10716694cb09f65a10d0dcf006310fd9f9245cddd599c87ecc9577fb994489f2c38dccb72ce705501ba979dd13c20568
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - "1.9.3"
4
+ - jruby-19mode # JRuby in 1.9 mode
5
+ script: bundle exec rspec spec
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in navigatrix.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Ben Eddy
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,144 @@
1
+ # Navigatrix
2
+ [![Build Status](https://api.travis-ci.org/foraker/navigatrix.png?branch=master)](http://travis-ci.org/foraker/navigatrix)
3
+ [![Code Climate](https://codeclimate.com/github/foraker/navigatrix.png)](https://codeclimate.com/github/foraker/navigatrix)
4
+
5
+ Navigation generation for Rails and Sinatra.
6
+
7
+ ## Installation
8
+ Add `gem "navigatrix", github: "foraker/navigatrix"` to your Gemfile and run `bundle install`.
9
+
10
+ ### The Simplest Possible Navigation
11
+ ```ERB
12
+ <%= render_navigation({
13
+ "Home" => "/",
14
+ "About Us" => "/about-us",
15
+ "Blog" => "/blog",
16
+ "Contact" => "/contact"
17
+ }) %>
18
+ ```
19
+ Assuming we're on the "/about-us" path, the resulting HTML will look like this:
20
+ ```HTML
21
+ <ul>
22
+ <li><a href="/">Home</a></li>
23
+ <li class="active">About Us</li>
24
+ <li><a href="/blog">Blog</a></li>
25
+ <li><a href="/contact">Contact</a></li>
26
+ </ul>
27
+ ```
28
+
29
+ ### A More Sophisticated Configuration
30
+ ```ERB
31
+ <%= render_navigation({
32
+ "Home" => "/",
33
+ "Users" => {
34
+ :path => "/users",
35
+ :active_states => [
36
+ {:path => "/my_account"},
37
+ {:path => /\/users\/\d*/},
38
+ ]
39
+ },
40
+ "Sign In" => {
41
+ :path => "/sign_in",
42
+ :render? => !user_signed_in?
43
+ },
44
+ "Sign Out" => {
45
+ :path => "/sign_out",
46
+ :render? => user_signed_in?
47
+ }
48
+ }, {
49
+ :active_class => "active-nav-item",
50
+ :html_attributes => {
51
+ :class => "nav"
52
+ }
53
+ }) %>
54
+ ```
55
+
56
+ Assuming we're on the "/users/1" path, and a User is signed in, the resulting HTML will look like this:
57
+ ```HTML
58
+ <ul class="nav">
59
+ <li><a href="/">Home</a></li>
60
+ <li class="active-nav-item">Users</li>
61
+ <li><a href="/sign_out">Sign Out</a></li>
62
+ </ul>
63
+ ```
64
+ The "Users" item is active and not linked because the path "/users/1" matches the pattern `/\/users\/\d*/`. The "Home" item is linked because we are not on the path "/".
65
+
66
+
67
+ ## List Item Configuration Options
68
+ ##### `:path`
69
+ Specifies where the navigation item should link to.
70
+
71
+ ##### `:active_states`
72
+ An array of state specification hashes used to control when a list item is considered "active". By default, when an item is active, it receives an HTML class of "active". For example:
73
+ ```Ruby
74
+ active_states: [
75
+ {controller: "users", actions: ["index"]},
76
+ {path: "/my-account"}
77
+ ]
78
+ ```
79
+ The first state specification dictates that the item will be active when the current controller is the `UsersController` and the current controller action is `index`. If we wanted the item to be active for any `UsersController` action, the specification should be `{controller: "users"}`.
80
+
81
+ The second state specification dictates that the item will be active with the current path is "/my-account". `:path` can be a string or regular expression.
82
+
83
+ ##### `:unlinked_states`
84
+ Also an array of state specification hashes (like `:active_states`). The state specifications determine if the item should be linked. By default, the item is unlinked if the current path is the same as the item path. The `:unlinked_states` option can be used to override this behavior.
85
+
86
+ ##### `:html_attributes`
87
+ HTML attributes added to an item.
88
+ ```Ruby
89
+ "Item 3" => {
90
+ :path => "/item_path"
91
+ :html_attributes => {:id => "nav-3"}
92
+ }
93
+ ```
94
+ Results in the following HTML.
95
+ ```HTML
96
+ <li id="nav-3"><a href="item_path">Item 3</a></li>
97
+ ```
98
+
99
+ ##### `:children`
100
+ Used for creating nested navigations. The `:children` should contain a navigation configuration.
101
+ ```Ruby
102
+ "Parent" => {
103
+ :path => "/parent_path"
104
+ :children => {
105
+ "Child 1" => "/child_1_path",
106
+ "Child 2" => {
107
+ :path => "/child_2_path",
108
+ :children => {
109
+ "Grandchild" => "/grandchild_path"
110
+ }
111
+ }
112
+ }
113
+ }
114
+ ```
115
+ Results in the following HTML.
116
+ ```HTML
117
+ <ul>
118
+ <li>
119
+ <a href="/parent_path">Parent</a>
120
+ <ul>
121
+ <li><a href="/child_1_path">Child 1</a></li>
122
+ <li>
123
+ <a href="/child_2_path">Child 2</a>
124
+ <ul>
125
+ <li href="/grandchild_path">Grandchild</li>
126
+ </ul>
127
+ </li>
128
+ </ul>
129
+ </li>
130
+ </ul>
131
+ ```
132
+
133
+ ##### `:render?`
134
+ Determines if the navigation item is rendered.
135
+
136
+ ## List Configuration Options
137
+ ##### `:html_attributes`
138
+ HTML attributes added to an the list.
139
+
140
+ ##### `:active_class`
141
+ Determines which HTML class is applied to list items when the item is active.
142
+
143
+ ##### `:inactive_class`
144
+ Determines which HTML class is applied to list items when the item is *not* active.
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,9 @@
1
+ require "navigatrix/version"
2
+ require "navigatrix/item"
3
+ require "navigatrix/item_collection"
4
+ require "navigatrix/renderer"
5
+ require 'navigatrix/integration/rails' if defined?(Rails)
6
+ require 'navigatrix/integration/sinatra' if defined?(Sinatra)
7
+
8
+ module Navigatrix
9
+ end
@@ -0,0 +1,51 @@
1
+ require "active_support/core_ext"
2
+
3
+ module Navigatrix
4
+ module Configuration
5
+ def self.wrap(wrapped)
6
+ wrapped.is_a?(Hash) ? AdvancedConfig.new(wrapped) : BasicConfig.new(wrapped)
7
+ end
8
+
9
+ class BasicConfig < Struct.new(:path)
10
+ def active_states
11
+ [{path: path}]
12
+ end
13
+
14
+ def unlinked_states
15
+ active_states
16
+ end
17
+
18
+ def children
19
+ {}
20
+ end
21
+
22
+ def html_attributes
23
+ {}
24
+ end
25
+
26
+ def render?
27
+ true
28
+ end
29
+ end
30
+
31
+ class AdvancedConfig < OpenStruct
32
+ DEFAULTS = {
33
+ :html_attributes => {},
34
+ :render? => true,
35
+ :children => {}
36
+ }
37
+
38
+ def initialize(raw_config)
39
+ super(DEFAULTS.merge(raw_config) || {})
40
+ end
41
+
42
+ def active_states
43
+ Array.wrap(super)
44
+ end
45
+
46
+ def unlinked_states
47
+ Array.wrap(super)
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,9 @@
1
+ require "navigatrix/view_helpers"
2
+
3
+ module Navigatrix
4
+ class Railtie < ::Rails::Railtie
5
+ initializer "navigatrix.view_helpers" do
6
+ ActionView::Base.send :include, Navigatrix::ViewHelpers
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,3 @@
1
+ require "navigatrix/view_helpers"
2
+
3
+ ::Sinatra::Application.send(:helpers, Navigatrix::ViewHelpers)
@@ -0,0 +1,109 @@
1
+ require "ostruct"
2
+ require 'forwardable'
3
+ require "navigatrix/configuration"
4
+
5
+ module Navigatrix
6
+ class Item < Struct.new(:name, :config, :context)
7
+ extend Forwardable
8
+ delegate :current_path => :context
9
+ delegate [:render?, :html_attributes] => :config
10
+
11
+ def active?
12
+ applicable_active_state? ||
13
+ active_children? ||
14
+ unlinked?
15
+ end
16
+
17
+ def linked?
18
+ !unlinked?
19
+ end
20
+
21
+ def unlinked?
22
+ unlinked_states_specified? ? unlinked_states.any?(&:applicable?) : currently_on_path?
23
+ end
24
+
25
+ def has_children?
26
+ children.any?
27
+ end
28
+
29
+ def children
30
+ @children ||= ItemCollection.new(config.children, context).items
31
+ end
32
+
33
+ def path
34
+ @path ||= Path.new(config.path).call(context).to_s
35
+ end
36
+
37
+ private
38
+
39
+ def active_children?
40
+ children.any?(&:active?)
41
+ end
42
+
43
+ def applicable_active_state?
44
+ active_states.any?(&:applicable?)
45
+ end
46
+
47
+ def currently_on_path?
48
+ current_path == path
49
+ end
50
+
51
+ def unlinked_states_specified?
52
+ unlinked_states.any?
53
+ end
54
+
55
+ def active_states
56
+ @active_states ||= config.active_states.map { |active_state| State.new(active_state, context) }
57
+ end
58
+
59
+ def unlinked_states
60
+ @unlinked_states ||= config.unlinked_states.map { |unlinked_state| State.new(unlinked_state, context) }
61
+ end
62
+
63
+ def config
64
+ @config ||= Configuration.wrap(super)
65
+ end
66
+
67
+ class Path < Struct.new(:source)
68
+ def call(context)
69
+ source.respond_to?(:call) ? source.call(context) : source
70
+ end
71
+ end
72
+
73
+ class State < OpenStruct
74
+ extend Forwardable
75
+ delegate [:current_path, :controller_name, :action_name] => :context
76
+
77
+ attr_reader :context
78
+
79
+ def initialize(config, context)
80
+ @context = context
81
+ super(config)
82
+ end
83
+
84
+ def applicable?
85
+ path ? path_matches? : (controller_matches? && action_matches?)
86
+ end
87
+
88
+ def controller_matches?
89
+ match?(controller, controller_name)
90
+ end
91
+
92
+ def action_matches?
93
+ actions.empty? || actions.include?(action_name)
94
+ end
95
+
96
+ def path_matches?
97
+ match?(path, current_path)
98
+ end
99
+
100
+ def match?(pattern, target)
101
+ pattern.is_a?(Regexp) ? target[pattern] : pattern == target
102
+ end
103
+
104
+ def actions
105
+ Array.wrap(super)
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,10 @@
1
+ module Navigatrix
2
+ class ItemCollection < Struct.new(:configuration, :context)
3
+ extend Forwardable
4
+ def_delegator :items, :each
5
+
6
+ def items
7
+ configuration.map { |name, config| Item.new(name, config, context) }
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,41 @@
1
+ require "navigatrix/rendering/context"
2
+ require "navigatrix/rendering/strategies/list"
3
+ require "navigatrix/rendering/strategies/bootstrap/navbar"
4
+ require "navigatrix/rendering/strategies/bootstrap/tabs"
5
+
6
+ module Navigatrix
7
+ class Renderer
8
+ attr_reader :configuration, :strategy, :render_context, :render_options
9
+
10
+ REGISTERED_STRATEGIES = {
11
+ :unordered_list => Rendering::Strategies::List,
12
+ :bootstrap_navbar => Rendering::Strategies::Bootstrap::Navbar,
13
+ :bootstrap_tabs => Rendering::Strategies::Bootstrap::Tabs
14
+ }
15
+
16
+ class MissingStrategy < NameError ; end
17
+
18
+ def initialize(configuration, options)
19
+ @configuration = configuration
20
+ @strategy = find_strategy(options.delete(:strategy))
21
+ @render_context = Rendering::Context.new(options.delete(:render_context))
22
+ @render_options = options
23
+ end
24
+
25
+ def render
26
+ strategy.new(item_collection.items, render_options).render
27
+ end
28
+
29
+ private
30
+
31
+ def item_collection
32
+ ItemCollection.new(configuration, render_context)
33
+ end
34
+
35
+ def find_strategy(strategy_or_name)
36
+ return strategy_or_name if strategy_or_name.is_a?(Class)
37
+ strategy_or_name ||= :unordered_list
38
+ REGISTERED_STRATEGIES[strategy_or_name] || raise(MissingStrategy, "can't find strategy #{strategy_name}")
39
+ end
40
+ end
41
+ end