navtastic 0.0.1
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 +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
|
+
[](https://circleci.com/gh/aramvisser/navtastic)
|
4
|
+
[](https://codeclimate.com/github/aramvisser/navtastic)
|
5
|
+
[](https://inch-ci.org/github/aramvisser/navtastic)
|
6
|
+
[](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
|