navtastic 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.circleci/config.yml +70 -0
- data/.codeclimate.yml +13 -0
- data/.gitignore +18 -0
- data/.rubocop.yml +21 -0
- data/.yardopts +4 -0
- data/CHANGELOG.md +14 -0
- data/Gemfile +3 -0
- data/LICENSE +21 -0
- data/README.md +143 -0
- data/Rakefile +19 -0
- data/lib/navtastic.rb +61 -0
- data/lib/navtastic/item.rb +49 -0
- data/lib/navtastic/menu.rb +127 -0
- data/lib/navtastic/renderer.rb +87 -0
- data/lib/navtastic/version.rb +3 -0
- data/navtastic.gemspec +25 -0
- data/spec/demo/index.rhtml +29 -0
- data/spec/demo/server.rb +54 -0
- data/spec/navtastic/item_spec.rb +14 -0
- data/spec/navtastic/menu_spec.rb +203 -0
- data/spec/navtastic/renderer_spec.rb +19 -0
- data/spec/navtastic_spec.rb +52 -0
- data/spec/spec_helper.rb +113 -0
- data/spec/support/matchers/current_item.rb +51 -0
- data/spec/support/navtastic_store.rb +6 -0
- metadata +162 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f71489809ad37489fff943b48ecc3c00224a7d5d
|
4
|
+
data.tar.gz: 8d52c7216084c2e84d620ae1f94b7464fc4c68ad
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c9a022d6a93b60adbc98dfed4d8fe26b9a9b4d35c6ac1e37a83e9a6c511cd4c021ed15e5fd4cc6871d40c585b032b13e6062c503550d11892fdccf7802d69015
|
7
|
+
data.tar.gz: 6b3678f6e320f10eab9f158845ec34cdb634f0b3babe35fa9c7649dfd907148a348438b6a7febb60bfef866fe185e76fe8225ff3b0b1cf0dc1b251750a9c7b85
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# Ruby CircleCI 2.0 configuration file
|
2
|
+
#
|
3
|
+
# Check https://circleci.com/docs/2.0/language-ruby/ for more details
|
4
|
+
#
|
5
|
+
version: 2
|
6
|
+
jobs:
|
7
|
+
build:
|
8
|
+
docker:
|
9
|
+
# specify the version you desire here
|
10
|
+
- image: circleci/ruby:2.4.1
|
11
|
+
|
12
|
+
# Specify service dependencies here if necessary
|
13
|
+
# CircleCI maintains a library of pre-built images
|
14
|
+
# documented at https://circleci.com/docs/2.0/circleci-images/
|
15
|
+
# - image: circleci/postgres:9.4
|
16
|
+
|
17
|
+
working_directory: ~/navtastic
|
18
|
+
|
19
|
+
steps:
|
20
|
+
- checkout
|
21
|
+
|
22
|
+
- run:
|
23
|
+
name: install dependencies
|
24
|
+
command: |
|
25
|
+
echo "gem 'rspec_junit_formatter'" >> Gemfile
|
26
|
+
echo "gem 'rubocop-junit-formatter'" >> Gemfile
|
27
|
+
echo "gem 'simplecov', require: false" >> Gemfile
|
28
|
+
|
29
|
+
bundle install --jobs=4 --retry=3 --path vendor/bundle
|
30
|
+
|
31
|
+
- run:
|
32
|
+
name: install code climate test reporter
|
33
|
+
command: |
|
34
|
+
curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
|
35
|
+
chmod +x ./cc-test-reporter
|
36
|
+
sudo mkdir -p $CIRCLE_TEST_REPORTS/phpunit
|
37
|
+
|
38
|
+
# run tests!
|
39
|
+
- run:
|
40
|
+
name: run tests
|
41
|
+
command: |
|
42
|
+
mkdir /tmp/test-results
|
43
|
+
|
44
|
+
./cc-test-reporter before-build
|
45
|
+
|
46
|
+
COVERAGE=true bundle exec rspec --format progress \
|
47
|
+
--format RspecJunitFormatter \
|
48
|
+
--out /tmp/test-results/rspec.xml \
|
49
|
+
--format progress
|
50
|
+
|
51
|
+
bundle exec rubocop \
|
52
|
+
--require $(bundle show rubocop-junit-formatter)/lib/rubocop/formatter/junit_formatter.rb \
|
53
|
+
--format RuboCop::Formatter::JUnitFormatter \
|
54
|
+
--out /tmp/test-results/rubocop.xml \
|
55
|
+
--format progress \
|
56
|
+
--parallel
|
57
|
+
|
58
|
+
- run:
|
59
|
+
name: upload reports to code climate
|
60
|
+
when: "always"
|
61
|
+
command: |
|
62
|
+
./cc-test-reporter format-coverage -t simplecov $CIRCLE_ARTIFACTS/coverage/.resultset.json
|
63
|
+
./cc-test-reporter upload-coverage
|
64
|
+
|
65
|
+
# collect reports
|
66
|
+
- store_test_results:
|
67
|
+
path: /tmp/test-results
|
68
|
+
- store_artifacts:
|
69
|
+
path: /tmp/test-results
|
70
|
+
destination: test-results
|
data/.codeclimate.yml
ADDED
data/.gitignore
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
/spec/reports/
|
4
|
+
/spec/examples.txt
|
5
|
+
|
6
|
+
## Documentation cache and generated files:
|
7
|
+
/.yardoc/
|
8
|
+
/_yardoc/
|
9
|
+
/doc/
|
10
|
+
/rdoc/
|
11
|
+
/coverage/
|
12
|
+
|
13
|
+
# for a library or gem, you might want to ignore these files since the code is
|
14
|
+
# intended to run in multiple environments; otherwise, check them in:
|
15
|
+
Gemfile.lock
|
16
|
+
.ruby-version
|
17
|
+
.ruby-gemset
|
18
|
+
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require: rubocop-rspec
|
2
|
+
|
3
|
+
AllCops:
|
4
|
+
Include:
|
5
|
+
- '**/Gemfile'
|
6
|
+
- '**/Rakefile'
|
7
|
+
Exclude:
|
8
|
+
- 'spec/demo/**/*'
|
9
|
+
- 'vendor/**/*'
|
10
|
+
TargetRubyVersion: 2.4
|
11
|
+
|
12
|
+
Metrics/BlockLength:
|
13
|
+
Exclude:
|
14
|
+
- 'spec/**/*_spec.rb'
|
15
|
+
- 'spec/support/matchers/**/*'
|
16
|
+
|
17
|
+
Style/FrozenStringLiteralComment:
|
18
|
+
Enabled: false
|
19
|
+
|
20
|
+
Style/StringLiterals:
|
21
|
+
Enabled: false
|
data/.yardopts
ADDED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
## [Unreleased]
|
4
|
+
|
5
|
+
## 0.0.1 - 2017-08-21
|
6
|
+
|
7
|
+
### Added
|
8
|
+
|
9
|
+
- Defining menu's and submenus
|
10
|
+
- Rendering a simple menu
|
11
|
+
- Highlighting current menu item
|
12
|
+
- Runtime parameters
|
13
|
+
|
14
|
+
[Unreleased]: https://github.com/aramvisser/navtastic/compare/v0.0.1...HEAD
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2017 Aram Visser
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
# Navtastic
|
2
|
+
|
3
|
+
[![Build Status](https://img.shields.io/circleci/project/github/aramvisser/navtastic.svg)](https://circleci.com/gh/aramvisser/navtastic)
|
4
|
+
[![Code Coverage](https://img.shields.io/codeclimate/coverage/github/aramvisser/navtastic.svg)](https://codeclimate.com/github/aramvisser/navtastic)
|
5
|
+
[![Inline docs](https://inch-ci.org/github/aramvisser/navtastic.svg?branch=master)](https://inch-ci.org/github/aramvisser/navtastic)
|
6
|
+
[![MIT License](https://img.shields.io/github/license/aramvisser/navtastic.svg)](https://github.com/aramvisser/navtastic/blob/master/LICENSE)
|
7
|
+
|
8
|
+
Navtastic is way to create and render complex navigation menus. It allows for runtime configurations
|
9
|
+
of menus.
|
10
|
+
|
11
|
+
- Keep menu content and rendering logic separate
|
12
|
+
- Automatically highlight the current page
|
13
|
+
|
14
|
+
## Table of Contents
|
15
|
+
|
16
|
+
- [Installation](#installation)
|
17
|
+
- [Example](#example)
|
18
|
+
- [Documentation](#documentation)
|
19
|
+
- [Submenus](#submenus)
|
20
|
+
- [Current item](#current-item)
|
21
|
+
- [Runtime parameters](#runtime-parameters)
|
22
|
+
|
23
|
+
## Installation
|
24
|
+
|
25
|
+
Add it to your Gemfile:
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
gem 'navtastic'
|
29
|
+
```
|
30
|
+
|
31
|
+
Run the following command to install it:
|
32
|
+
|
33
|
+
```console
|
34
|
+
bundle install
|
35
|
+
```
|
36
|
+
|
37
|
+
## Example
|
38
|
+
|
39
|
+
Define a menu somwhere:
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
Navtastic.define :main_menu do |menu|
|
43
|
+
menu.item "Home", '/'
|
44
|
+
menu.item "Posts", '/posts'
|
45
|
+
menu.item "About", '/about'
|
46
|
+
end
|
47
|
+
```
|
48
|
+
|
49
|
+
Render it in your partials:
|
50
|
+
|
51
|
+
```erb
|
52
|
+
<%= Navtastic.render :main_menu, current_url %>
|
53
|
+
```
|
54
|
+
|
55
|
+
Using the default renderer, assuming that the current url starts with `/posts`, will result in:
|
56
|
+
|
57
|
+
```html
|
58
|
+
<ul>
|
59
|
+
<li>
|
60
|
+
<a href="/">Home</a>
|
61
|
+
</li>
|
62
|
+
<li class="current">
|
63
|
+
<a href="/posts">Posts</a>
|
64
|
+
</li>
|
65
|
+
<li>
|
66
|
+
<a href="/about">About</a>
|
67
|
+
</li>
|
68
|
+
</ul>
|
69
|
+
```
|
70
|
+
|
71
|
+
## Documentation
|
72
|
+
|
73
|
+
## Submenus
|
74
|
+
|
75
|
+
Every item can have a submenu. They can be nested as deeply as you want (or at least until Ruby starts complaining).
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
Navtastic.define :main_menu do |menu|
|
79
|
+
menu.item "Home", '/' do |submenu|
|
80
|
+
submenu.item "Posts", '/posts'
|
81
|
+
submenu.item "About", '/about'
|
82
|
+
end
|
83
|
+
|
84
|
+
menu.item "Settings" do |submenu|
|
85
|
+
submenu.item "General", '/settings'
|
86
|
+
submenu.item "Profile", '/settings/profile'
|
87
|
+
end
|
88
|
+
end
|
89
|
+
```
|
90
|
+
|
91
|
+
By default, submenus will be rendered inside the `<li>` tag of the parent item.
|
92
|
+
|
93
|
+
```html
|
94
|
+
<ul>
|
95
|
+
<li>
|
96
|
+
<a href="/">Parent</a>
|
97
|
+
<ul>
|
98
|
+
<li>
|
99
|
+
<a href="/child">Child</a>
|
100
|
+
</li>
|
101
|
+
</ul>
|
102
|
+
</li>
|
103
|
+
</ul>
|
104
|
+
```
|
105
|
+
|
106
|
+
### Current item
|
107
|
+
|
108
|
+
The current active item is decided by the `current_url` parameter when rendering a menu. It is the
|
109
|
+
item with the longest url that starts with the current_url.
|
110
|
+
|
111
|
+
For example, if there is a menu containing these urls:
|
112
|
+
|
113
|
+
- `/`
|
114
|
+
- `/posts`
|
115
|
+
- `/posts/featured`
|
116
|
+
|
117
|
+
If the current_url is `/posts/featured/2017`, the `/posts/featured` item will be highlighted. If the
|
118
|
+
current_url is `/posts/123`, then `/posts` is highlighted.
|
119
|
+
|
120
|
+
The root url `/` will always match, if no other items match the current _url. If there is no item
|
121
|
+
with `/` as url in the menu and no other urls match, nothing will be highlighted.
|
122
|
+
|
123
|
+
### Runtime parameters
|
124
|
+
|
125
|
+
You can pass runtime parameters when defining a menu. For example, passing the current user and
|
126
|
+
change the menu accordingly.
|
127
|
+
|
128
|
+
```ruby
|
129
|
+
# Define the menu
|
130
|
+
Navtastic.define :main_menu do |menu, params|
|
131
|
+
menu.item "Home", "/"
|
132
|
+
|
133
|
+
if params[:current_user]
|
134
|
+
menu.item "Profile", "/users/#{params[:current_user].id}"
|
135
|
+
menu.item "Logout", "/logout"
|
136
|
+
else
|
137
|
+
menu.item "Login", "/login"
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# Render it with the current user as a parameter
|
142
|
+
Navtastic.render :main_menu, current_url, current_user: User.current
|
143
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'rspec/core/rake_task'
|
2
|
+
require 'rubocop/rake_task'
|
3
|
+
require 'yard'
|
4
|
+
require './spec/demo/server'
|
5
|
+
|
6
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
7
|
+
t.rspec_opts = "--order rand"
|
8
|
+
end
|
9
|
+
|
10
|
+
RuboCop::RakeTask.new
|
11
|
+
|
12
|
+
YARD::Rake::YardocTask.new
|
13
|
+
|
14
|
+
desc "Run the demo server"
|
15
|
+
task :demo do
|
16
|
+
DemoServer.new(9090).start
|
17
|
+
end
|
18
|
+
|
19
|
+
task default: %i[spec rubocop]
|
data/lib/navtastic.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'navtastic/item'
|
2
|
+
require 'navtastic/menu'
|
3
|
+
require 'navtastic/renderer'
|
4
|
+
require 'navtastic/version'
|
5
|
+
|
6
|
+
# Main module containing some convenience methods
|
7
|
+
module Navtastic
|
8
|
+
# @private
|
9
|
+
#
|
10
|
+
# @return [Hash<Object,Block>] all stored menus
|
11
|
+
@menu_store = {}
|
12
|
+
|
13
|
+
# Define a new menu to be rendered later
|
14
|
+
#
|
15
|
+
# @example Define a new menu
|
16
|
+
# Navtastic.define :main do |menu, params|
|
17
|
+
# menu.item "Home", "/"
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# @param name the name of the menu
|
21
|
+
#
|
22
|
+
# @yield [menu, params] block to generate a new menu
|
23
|
+
# @yieldparam menu [Menu] the menu to be initialized
|
24
|
+
# @yieldparam params [Hash] runtime parameters
|
25
|
+
#
|
26
|
+
# @raise [ArgumentError] if no block was given
|
27
|
+
def self.define(name, &block)
|
28
|
+
raise ArgumentError, "no block given" unless block_given?
|
29
|
+
|
30
|
+
name = name.to_sym if name.is_a? String
|
31
|
+
@menu_store[name] = block
|
32
|
+
end
|
33
|
+
|
34
|
+
# Render a stored menu
|
35
|
+
#
|
36
|
+
# @param name the name of the defined menu
|
37
|
+
# @param current_url [String] the url of the current page
|
38
|
+
# @param params [Hash] runtime parameters
|
39
|
+
#
|
40
|
+
# @raise [KeyError] if the menu was not defined
|
41
|
+
#
|
42
|
+
# @return [Renderer] the renderer for the menu
|
43
|
+
def self.render(name, current_url, params = {})
|
44
|
+
name = name.to_sym if name.is_a? String
|
45
|
+
block = @menu_store[name]
|
46
|
+
|
47
|
+
raise KeyError, "menu not defined: #{name.inspect}" if block.nil?
|
48
|
+
|
49
|
+
menu = Menu.new
|
50
|
+
block.call(menu, params)
|
51
|
+
menu.current_url = current_url
|
52
|
+
Renderer.render(menu)
|
53
|
+
end
|
54
|
+
|
55
|
+
# A list of all defined menus
|
56
|
+
#
|
57
|
+
# @return [Array]
|
58
|
+
def self.all_menus
|
59
|
+
@menu_store.keys
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Navtastic
|
2
|
+
# A single menu item
|
3
|
+
class Item
|
4
|
+
# @return [String] the name to be displayed in the menu
|
5
|
+
attr_reader :name
|
6
|
+
|
7
|
+
# @return [String,nil] the url to link to if item is a link, nil otherwise
|
8
|
+
attr_reader :url
|
9
|
+
|
10
|
+
# @return [Menu,nil] the submenu of this item, if defined
|
11
|
+
attr_accessor :submenu
|
12
|
+
|
13
|
+
# Create a new item
|
14
|
+
#
|
15
|
+
# This should not be used directly. Use the {Menu#item} method instead.
|
16
|
+
#
|
17
|
+
# @private
|
18
|
+
#
|
19
|
+
# @param menu [Menu] the menu this items belongs to
|
20
|
+
# @param name [String] the name to display when rendering
|
21
|
+
# @param url [String] the url to link to, if the item is a link
|
22
|
+
def initialize(menu, name, url = nil)
|
23
|
+
@menu = menu
|
24
|
+
@name = name
|
25
|
+
@url = url
|
26
|
+
|
27
|
+
@submenu = nil
|
28
|
+
end
|
29
|
+
|
30
|
+
# Check if this item is the current item in the menu
|
31
|
+
#
|
32
|
+
# @see file:README.md#Current_item documentation on how the current item is
|
33
|
+
# selected
|
34
|
+
#
|
35
|
+
# @return [Bool] if the item is the current item
|
36
|
+
def current?
|
37
|
+
@menu.current_item == self
|
38
|
+
end
|
39
|
+
|
40
|
+
# @return [Bool] true if the item has a submenu, false other
|
41
|
+
def submenu?
|
42
|
+
!@submenu.nil?
|
43
|
+
end
|
44
|
+
|
45
|
+
def inspect
|
46
|
+
"#<Item \"#{name}\" [#{url}] current?:#{current?}>"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|