├── uninstall.rb ├── gemfiles ├── .bundle │ └── config ├── rails-4-1-stable.gemfile ├── rails-4-2-stable.gemfile ├── rails-5-2-stable.gemfile ├── rails-6-0-stable.gemfile ├── rails-6-1-stable.gemfile └── rails-3-2-stable.gemfile ├── Gemfile ├── init.rb ├── lib ├── simple-navigation.rb ├── simple_navigation │ ├── version.rb │ ├── railtie.rb │ ├── adapters.rb │ ├── renderer.rb │ ├── adapters │ │ ├── padrino.rb │ │ ├── base.rb │ │ ├── nanoc.rb │ │ ├── sinatra.rb │ │ └── rails.rb │ ├── renderer │ │ ├── text.rb │ │ ├── json.rb │ │ ├── links.rb │ │ ├── list.rb │ │ ├── breadcrumbs.rb │ │ └── base.rb │ ├── config_file.rb │ ├── config_file_finder.rb │ ├── items_provider.rb │ ├── item_adapter.rb │ ├── configuration.rb │ ├── item.rb │ ├── item_container.rb │ └── helpers.rb ├── generators │ └── navigation_config │ │ └── navigation_config_generator.rb └── simple_navigation.rb ├── .rspec ├── spec ├── initializers │ ├── coveralls.rb │ ├── rails.rb │ ├── rspec.rb │ ├── memfs.rb │ └── have_css_matcher.rb ├── fake_app │ ├── config │ │ └── navigation.rb │ └── rails_app.rb ├── integration │ └── rendering_navigation_spec.rb ├── simple_navigation │ ├── config_file_spec.rb │ ├── adapters │ │ ├── padrino_spec.rb │ │ ├── sinatra_spec.rb │ │ └── rails_spec.rb │ ├── items_provider_spec.rb │ ├── renderer │ │ ├── text_spec.rb │ │ ├── json_spec.rb │ │ ├── links_spec.rb │ │ ├── list_spec.rb │ │ ├── breadcrumbs_spec.rb │ │ └── base_spec.rb │ ├── config_file_finder_spec.rb │ ├── configuration_spec.rb │ ├── item_adapter_spec.rb │ ├── helpers_spec.rb │ ├── item_spec.rb │ └── item_container_spec.rb ├── spec_helper.rb └── simple_navigation_spec.rb ├── .gitignore ├── generators └── navigation_config │ ├── USAGE │ ├── navigation_config_generator.rb │ └── templates │ └── config │ └── navigation.rb ├── install.rb ├── Guardfile ├── .travis.yml ├── Rakefile ├── LICENSE ├── simple-navigation.gemspec ├── README.md └── CHANGELOG.md /uninstall.rb: -------------------------------------------------------------------------------- 1 | # Uninstall hook code here 2 | -------------------------------------------------------------------------------- /gemfiles/.bundle/config: -------------------------------------------------------------------------------- 1 | --- 2 | BUNDLE_JOBS: 4 3 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gemspec 4 | -------------------------------------------------------------------------------- /init.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/rails/init' 2 | -------------------------------------------------------------------------------- /lib/simple-navigation.rb: -------------------------------------------------------------------------------- 1 | require 'simple_navigation' 2 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --format=documentation 3 | --require spec_helper 4 | -------------------------------------------------------------------------------- /spec/initializers/coveralls.rb: -------------------------------------------------------------------------------- 1 | require 'coveralls' 2 | 3 | Coveralls.wear! 4 | -------------------------------------------------------------------------------- /spec/initializers/rails.rb: -------------------------------------------------------------------------------- 1 | begin 2 | require 'rails' 3 | rescue LoadError 4 | end 5 | -------------------------------------------------------------------------------- /lib/simple_navigation/version.rb: -------------------------------------------------------------------------------- 1 | module SimpleNavigation 2 | VERSION = '4.4.0' 3 | end 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | rdoc 3 | pkg 4 | coverage 5 | Gemfile.lock 6 | .rvmrc 7 | capybara-* 8 | gemfiles/*.lock 9 | log 10 | spec/fake_app/tmp 11 | -------------------------------------------------------------------------------- /generators/navigation_config/USAGE: -------------------------------------------------------------------------------- 1 | Creates a template config file for the simple-navigation plugin. You will find the generated file in config/navigation.rb. -------------------------------------------------------------------------------- /spec/initializers/rspec.rb: -------------------------------------------------------------------------------- 1 | RSpec.configure do |config| 2 | config.expect_with(:rspec) do |c| 3 | c.syntax = :expect 4 | end 5 | 6 | config.order = :random 7 | end 8 | -------------------------------------------------------------------------------- /spec/initializers/memfs.rb: -------------------------------------------------------------------------------- 1 | require 'memfs' 2 | 3 | RSpec.configure do |config| 4 | config.around(memfs: true) do |example| 5 | MemFs.activate { example.run } 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /gemfiles/rails-4-1-stable.gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'rake', '< 11.0' 4 | gem 'railties', '~> 4.1.0' 5 | gem 'rspec-rails', '~> 3.2.1' 6 | 7 | gemspec path: '../' 8 | -------------------------------------------------------------------------------- /gemfiles/rails-4-2-stable.gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'rake', '< 11.0' 4 | gem 'railties', '~> 4.2.0' 5 | gem 'rspec-rails', '~> 3.2.1' 6 | 7 | gemspec path: '../' 8 | -------------------------------------------------------------------------------- /gemfiles/rails-5-2-stable.gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'rake', '< 11.0' 4 | gem 'railties', '~> 5.2.0' 5 | gem 'rspec-rails', '~> 3.8.2' 6 | 7 | gemspec path: '../' 8 | -------------------------------------------------------------------------------- /install.rb: -------------------------------------------------------------------------------- 1 | begin 2 | puts IO.read(File.join(File.dirname(__FILE__), 'README')) 3 | rescue Exception => e 4 | puts "The following error ocurred while installing the plugin: #{e.message}" 5 | end 6 | -------------------------------------------------------------------------------- /gemfiles/rails-6-0-stable.gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'rake', '< 11.0' 4 | 5 | # not yet 6 | gem 'railties', '~> 6.0.0' 7 | gem 'rspec-rails', '~> 3.8.2' 8 | 9 | gemspec path: '../' 10 | -------------------------------------------------------------------------------- /gemfiles/rails-6-1-stable.gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'rake', '< 11.0' 4 | 5 | # not yet 6 | gem 'railties', '~> 6.1.0' 7 | gem 'rspec-rails', '~> 3.8.2' 8 | 9 | gemspec path: '../' 10 | -------------------------------------------------------------------------------- /lib/simple_navigation/railtie.rb: -------------------------------------------------------------------------------- 1 | module SimpleNavigation 2 | class Railtie < ::Rails::Railtie 3 | initializer 'simple_navigation.register' do |app| 4 | SimpleNavigation.register 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /generators/navigation_config/navigation_config_generator.rb: -------------------------------------------------------------------------------- 1 | class NavigationConfigGenerator < Rails::Generator::Base 2 | def manifest 3 | record do |m| 4 | m.file 'config/navigation.rb', 'config/navigation.rb' 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /gemfiles/rails-3-2-stable.gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'rake', '< 11.0' 4 | gem 'railties', '~> 3.2.0' 5 | gem 'rspec-rails', '~> 3.2.1' 6 | 7 | if RUBY_VERSION >= '2.2.0' 8 | gem 'test-unit', '~> 3.0' 9 | end 10 | 11 | gemspec path: '../' 12 | -------------------------------------------------------------------------------- /Guardfile: -------------------------------------------------------------------------------- 1 | guard :rspec, all_after_pass: true, failed_mode: :none do 2 | watch(%r{^spec/.+_spec\.rb$}) 3 | watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" } 4 | watch(%r{^spec/fake_app/.+\.rb$}) { 'spec/integration' } 5 | 6 | watch('spec/spec_helper.rb') { 'spec' } 7 | end 8 | -------------------------------------------------------------------------------- /spec/fake_app/config/navigation.rb: -------------------------------------------------------------------------------- 1 | SimpleNavigation::Configuration.run do |navigation| 2 | navigation.items do |nav| 3 | nav.item :item_1, 'Item 1', '/item_1', html: {class: 'item_1'}, link_html: {id: 'link_1'} 4 | nav.item :item_2, 'Item 2', '/item_2', html: {class: 'item_2'}, link_html: {id: 'link_2'} 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /lib/simple_navigation/adapters.rb: -------------------------------------------------------------------------------- 1 | require 'simple_navigation/adapters/base' 2 | 3 | module SimpleNavigation 4 | module Adapters 5 | autoload :Rails, 'simple_navigation/adapters/rails' 6 | autoload :Padrino, 'simple_navigation/adapters/padrino' 7 | autoload :Sinatra, 'simple_navigation/adapters/sinatra' 8 | autoload :Nanoc, 'simple_navigation/adapters/nanoc' 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /lib/simple_navigation/renderer.rb: -------------------------------------------------------------------------------- 1 | require 'simple_navigation/helpers' 2 | require 'simple_navigation/renderer/base' 3 | 4 | module SimpleNavigation 5 | module Renderer 6 | autoload :List, 'simple_navigation/renderer/list' 7 | autoload :Links, 'simple_navigation/renderer/links' 8 | autoload :Breadcrumbs, 'simple_navigation/renderer/breadcrumbs' 9 | autoload :Text, 'simple_navigation/renderer/text' 10 | autoload :Json, 'simple_navigation/renderer/json' 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /spec/integration/rendering_navigation_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.feature 'Rendering navigation' do 2 | background do 3 | SimpleNavigation.set_env(RailsApp::Application.root, 'test') 4 | end 5 | 6 | scenario 'Rendering basic navigation', type: :feature do 7 | visit '/base_spec' 8 | 9 | expect(page).to have_content('Item 1') 10 | expect(page).to have_content('Item 2') 11 | expect(page).to have_selector('li.item_1 a#link_1') 12 | expect(page).to have_selector('li.item_2 a#link_2') 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /lib/simple_navigation/adapters/padrino.rb: -------------------------------------------------------------------------------- 1 | module SimpleNavigation 2 | module Adapters 3 | class Padrino < Sinatra 4 | def self.register(app) 5 | SimpleNavigation.set_env(::Padrino.root, ::Padrino.env) 6 | ::Padrino::Application.send(:helpers, SimpleNavigation::Helpers) 7 | end 8 | 9 | def link_to(name, url, options = {}) 10 | context.link_to(name, url, options) 11 | end 12 | 13 | def content_tag(type, content, options = {}) 14 | context.content_tag(type, content.html_safe, options) 15 | end 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /spec/initializers/have_css_matcher.rb: -------------------------------------------------------------------------------- 1 | RSpec::Matchers.define :have_css do |expected, times| 2 | match do |actual| 3 | selector = Nokogiri::HTML(actual).css(expected) 4 | 5 | if times 6 | expect(selector.size).to eq times 7 | else 8 | expect(selector.size).to be >= 1 9 | end 10 | end 11 | 12 | failure_message do |actual| 13 | "expected #{actual.to_s} to have #{times || 1} elements matching '#{expected}'" 14 | end 15 | 16 | failure_message_when_negated do |actual| 17 | "expected #{actual.to_s} not to have #{times || 1} elements matching '#{expected}'" 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | before_install: 2 | - gem install bundler 3 | 4 | matrix: 5 | include: 6 | - rvm: 2.5.8 7 | gemfile: gemfiles/rails-6-1-stable.gemfile 8 | - rvm: 2.6.7 9 | gemfile: gemfiles/rails-6-1-stable.gemfile 10 | - rvm: 2.7.3 11 | gemfile: gemfiles/rails-6-1-stable.gemfile 12 | - rvm: 2.5.3 13 | gemfile: gemfiles/rails-6-0-stable.gemfile 14 | - rvm: 2.6.0 15 | gemfile: gemfiles/rails-6-0-stable.gemfile 16 | - rvm: 2.5.1 17 | gemfile: gemfiles/rails-5-2-stable.gemfile 18 | - rvm: 2.4.5 19 | gemfile: gemfiles/rails-4-2-stable.gemfile 20 | - rvm: 2.3.3 21 | gemfile: gemfiles/rails-4-1-stable.gemfile 22 | - rvm: 2.3.3 23 | gemfile: gemfiles/rails-3-2-stable.gemfile 24 | -------------------------------------------------------------------------------- /lib/generators/navigation_config/navigation_config_generator.rb: -------------------------------------------------------------------------------- 1 | class NavigationConfigGenerator < Rails::Generators::Base 2 | def self.source_root 3 | @source_root ||= begin 4 | tpl_dir = %w[.. .. .. .. generators navigation_config templates] 5 | tpl_dir_path = File.join(tpl_dir) 6 | File.expand_path(tpl_dir_path, __FILE__) 7 | end 8 | end 9 | 10 | desc 'Creates a template config file for the simple-navigation plugin. ' \ 11 | 'You will find the generated file in config/navigation.rb.' 12 | def navigation_config 13 | copy_file('config/navigation.rb', 'config/navigation.rb') 14 | readme_path = File.join(%w[.. .. .. .. README.md]) 15 | say File.read(File.expand_path(readme_path, __FILE__)) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/simple_navigation/renderer/text.rb: -------------------------------------------------------------------------------- 1 | module SimpleNavigation 2 | module Renderer 3 | # Renders the 'chain' of selected navigation items as simple text items, 4 | # joined with an optional separator (similar to breadcrumbs, but without 5 | # markup). 6 | class Text < SimpleNavigation::Renderer::Base 7 | def render(item_container) 8 | list(item_container).compact.join(options[:join_with] || ' ') 9 | end 10 | 11 | private 12 | 13 | def list(item_container) 14 | item_container.items.keep_if(&:selected?).map do |item| 15 | [item.name(apply_generator: false)] + 16 | (include_sub_navigation?(item) ? list(item.sub_navigation) : []) 17 | end 18 | end 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /spec/simple_navigation/config_file_spec.rb: -------------------------------------------------------------------------------- 1 | require 'simple_navigation/config_file' 2 | 3 | module SimpleNavigation 4 | describe ConfigFile do 5 | subject(:config_file) { ConfigFile.new(context) } 6 | 7 | let(:context) { :default } 8 | 9 | describe '#name' do 10 | context 'when the context is :default' do 11 | it 'returns navigation.rb' do 12 | expect(config_file.name).to eq 'navigation.rb' 13 | end 14 | end 15 | 16 | context 'when the context is different from :default' do 17 | let(:context) { :HelloWorld } 18 | 19 | it 'returns UNDERSCORED_CONTEXT_navigation.rb' do 20 | expect(config_file.name).to eq 'hello_world_navigation.rb' 21 | end 22 | end 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/simple_navigation/renderer/json.rb: -------------------------------------------------------------------------------- 1 | require 'json' 2 | 3 | module SimpleNavigation 4 | module Renderer 5 | # Renders the navigation items as a object tree serialized as a json string, 6 | # can also output raw ruby Hashes 7 | class Json < SimpleNavigation::Renderer::Base 8 | def render(item_container) 9 | results = hash_render(item_container) 10 | options[:as_hash] ? results : results.to_json 11 | end 12 | 13 | private 14 | 15 | def hash_render(item_container) 16 | return nil unless item_container 17 | 18 | item_container.items.map do |item| 19 | { 20 | items: hash_render(item.sub_navigation), 21 | name: item.name, 22 | selected: item.selected?, 23 | url: item.url, 24 | options: item.options 25 | } 26 | end 27 | end 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /spec/fake_app/rails_app.rb: -------------------------------------------------------------------------------- 1 | ENV['RAILS_ENV'] ||= 'test' 2 | 3 | require 'action_controller/railtie' 4 | require 'simple_navigation' 5 | 6 | module RailsApp 7 | class Application < Rails::Application 8 | config.active_support.deprecation = :log 9 | config.cache_classes = true 10 | config.eager_load = false 11 | config.root = __dir__ 12 | config.secret_token = 'x'*100 13 | config.session_store :cookie_store, key: '_myapp_session' 14 | end 15 | 16 | class TestsController < ActionController::Base 17 | def base 18 | render inline: <<-END 19 | 20 | 21 | 22 | <%= render_navigation %> 23 | 24 | 25 | END 26 | end 27 | end 28 | end 29 | 30 | Rails.backtrace_cleaner.remove_silencers! 31 | RailsApp::Application.initialize! 32 | 33 | RailsApp::Application.routes.draw do 34 | get '/base_spec' => 'rails_app/tests#base' 35 | end 36 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler' 2 | Bundler::GemHelper.install_tasks 3 | 4 | require 'rspec/core' 5 | require 'rspec/core/rake_task' 6 | 7 | require 'rdoc/task' 8 | 9 | RSpec::Core::RakeTask.new(:spec) 10 | 11 | task default: 'spec' 12 | 13 | namespace :spec do 14 | mappers = %w[ 15 | rails-3-2-stable 16 | rails-4-1-stable 17 | rails-4-2-stable 18 | rails-5-2-stable 19 | rails-6-0-stable 20 | rails-6-1-stable 21 | ] 22 | 23 | mappers.each do |gemfile| 24 | desc "Run Tests against #{gemfile}" 25 | task gemfile do 26 | sh "BUNDLE_GEMFILE='gemfiles/#{gemfile}.gemfile' bundle -j 4 --quiet" 27 | sh "BUNDLE_GEMFILE='gemfiles/#{gemfile}.gemfile' bundle exec rake -t spec" 28 | end 29 | end 30 | 31 | desc 'Run Tests against all ORMs' 32 | task all: mappers 33 | end 34 | 35 | RDoc::Task.new do |rdoc| 36 | rdoc.rdoc_dir = 'rdoc' 37 | rdoc.title = 'SimpleNavigation' 38 | rdoc.options << '--inline-source' 39 | rdoc.rdoc_files.include('README.md', 'lib/**/*.rb') 40 | end 41 | -------------------------------------------------------------------------------- /spec/simple_navigation/adapters/padrino_spec.rb: -------------------------------------------------------------------------------- 1 | module SimpleNavigation 2 | module Adapters 3 | describe Padrino do 4 | let(:adapter) { SimpleNavigation::Adapters::Padrino.new(context) } 5 | let(:content) { double(:content) } 6 | let(:context) { double(:context, request: request) } 7 | let(:request) { double(:request) } 8 | 9 | describe '#link_to' do 10 | it 'delegates to context' do 11 | expect(context).to receive(:link_to) 12 | .with('name', 'url', :my_option => true) 13 | adapter.link_to('name', 'url', :my_option => true) 14 | end 15 | end 16 | 17 | describe '#content_tag' do 18 | it 'delegates to context' do 19 | expect(content).to receive(:html_safe).and_return('content') 20 | expect(context).to receive(:content_tag) 21 | .with('type', 'content', my_option: true) 22 | adapter.content_tag('type', content, my_option: true) 23 | end 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 codeplant GmbH 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. 23 | -------------------------------------------------------------------------------- /lib/simple_navigation/config_file.rb: -------------------------------------------------------------------------------- 1 | require 'active_support/core_ext/string' 2 | 3 | module SimpleNavigation 4 | # Internal: Encapsulates the config file naming knowledge. 5 | class ConfigFile 6 | # Internal: Initializes a ConfigFile. 7 | # 8 | # context - The navigation context for this ConfigFile. 9 | def initialize(context) 10 | @prefix = prefix_for_context(context) 11 | end 12 | 13 | # Internal: Returns the name of the configuration file on disk. 14 | # 15 | # Based on the the initialization context the outcome may differ. 16 | # 17 | # Examples 18 | # 19 | # ConfigFile.new.name # => "navigation.rb" 20 | # ConfigFile.new(:default).name # => "navigation.rb" 21 | # ConfigFile.new(:other).name # => "other_navigation.rb" 22 | # 23 | # Returns a String representing the name of the configuration file on disk. 24 | def name 25 | @name ||= "#{prefix}navigation.rb" 26 | end 27 | 28 | private 29 | 30 | attr_reader :prefix 31 | 32 | def prefix_for_context(context) 33 | context == :default ? '' : "#{context.to_s.underscore}_" 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /lib/simple_navigation/adapters/base.rb: -------------------------------------------------------------------------------- 1 | module SimpleNavigation 2 | module Adapters 3 | # This is the base class for all adapters. 4 | # This class mainly exists for documenting reasons. 5 | # It lists all the methods that an adapter should implement. 6 | # 7 | class Base 8 | attr_reader :context, :request 9 | 10 | # This method is usually called when the framework is initialized. 11 | # It should call SimpleNavigation.set_env and install 12 | # SimpleNavigation::Helpers where appropriate. 13 | def self.register; end 14 | 15 | # Returns the full path incl. query params 16 | def request_uri; end 17 | 18 | # Returns the path without query params 19 | def request_path; end 20 | 21 | # Returns the context in which the config files will be evaluated 22 | def context_for_eval; end 23 | 24 | # Returns true if the current request's url matches the specified url. 25 | # Used to determine if an item should be autohighlighted. 26 | def current_page?(url); end 27 | 28 | # Returns a link with the specified name, url and options. 29 | # Used for rendering. 30 | def link_to(name, url, options = {}); end 31 | 32 | # Returns a tag of the specified type, content and options. 33 | # Used for rendering. 34 | def content_tag(type, content, options = {}); end 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /lib/simple_navigation/config_file_finder.rb: -------------------------------------------------------------------------------- 1 | require 'simple_navigation/config_file' 2 | 3 | module SimpleNavigation 4 | # Internal: Encapsulates the configuration file finding logic. 5 | class ConfigFileFinder 6 | # Internal: Initializes a ConfigFileFinder. 7 | # 8 | # paths - an enumerable list of paths in which to look for configuration 9 | # files. 10 | def initialize(paths) 11 | @paths = paths 12 | end 13 | 14 | # Internal: Searches a configuration file for the given context in the 15 | # initialization paths. 16 | # 17 | # context - The navigation context for which to look the configuration file. 18 | # 19 | # Returns a String representing the full path of the configuation file. 20 | # Raises StandardError if no file is found. 21 | def find(context) 22 | config_file_name = config_file_name_for_context(context) 23 | 24 | find_config_file(config_file_name) || 25 | fail("Config file '#{config_file_name}' not found in " \ 26 | "path(s) #{paths.join(', ')}!") 27 | end 28 | 29 | private 30 | 31 | attr_reader :paths 32 | 33 | def config_file_name_for_context(context) 34 | ConfigFile.new(context).name 35 | end 36 | 37 | def find_config_file(config_file_name) 38 | paths.map { |path| File.join(path, config_file_name) } 39 | .find { |full_path| File.exist?(full_path) } 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /lib/simple_navigation/items_provider.rb: -------------------------------------------------------------------------------- 1 | module SimpleNavigation 2 | # Acts as a proxy to navigation items that are passed into the 3 | # SimpleNavigation::Configuration#items method. 4 | # It hides the logic for finding items from the Configuration object. 5 | # 6 | class ItemsProvider 7 | attr_reader :provider 8 | 9 | # It accepts the following types of provider: 10 | # * methodname as symbol - the specified method should return the relevant 11 | # items and has to be available in the view (a helper method) 12 | # * object that responds to :items 13 | # * enumerable object that represents the items 14 | # 15 | # See SimpleNavigation::ItemAdapter for the requirements that need to be 16 | # fulfilled by the provided items. 17 | # 18 | def initialize(provider) 19 | @provider = provider 20 | end 21 | 22 | # Returns the navigation items 23 | def items 24 | if provider.is_a?(Symbol) 25 | SimpleNavigation.context_for_eval.send(provider) 26 | elsif provider.respond_to?(:items) 27 | provider.items 28 | elsif provider.respond_to?(:each) 29 | provider 30 | else 31 | fail('items_provider either must be a symbol specifying the ' \ 32 | 'helper-method to call, an object with an items-method defined ' \ 33 | 'or an enumerable representing the items') 34 | end 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /lib/simple_navigation/adapters/nanoc.rb: -------------------------------------------------------------------------------- 1 | module SimpleNavigation 2 | module Adapters 3 | class Nanoc < Base 4 | class << self 5 | def register(root) 6 | SimpleNavigation.set_env(root, 'development') 7 | Nanoc3::Context.send(:include, SimpleNavigation::Helpers) 8 | end 9 | end 10 | 11 | def initialize(ctx) 12 | @context = ctx 13 | end 14 | 15 | # Returns the context in which the config files will be evaluated 16 | def context_for_eval 17 | context 18 | end 19 | 20 | # Returns true if the current request's url matches the specified url. 21 | # Used to determine if an item should be autohighlighted. 22 | def current_page?(url) 23 | path = context.item.path 24 | path && path.chop == url 25 | end 26 | 27 | # Returns a link with the specified name, url and options. 28 | # Used for rendering. 29 | def link_to(name, url, options = {}) 30 | "#{name}" 31 | end 32 | 33 | # Returns a tag of the specified type, content and options. 34 | # Used for rendering. 35 | def content_tag(type, content, options = {}) 36 | "<#{type} #{to_attributes(options)}>#{content}" 37 | end 38 | 39 | private 40 | 41 | def to_attributes(options) 42 | options.map { |k, v| v.nil? ? nil : "#{k}='#{v}'" }.compact.join(' ') 43 | end 44 | end 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /lib/simple_navigation/renderer/links.rb: -------------------------------------------------------------------------------- 1 | module SimpleNavigation 2 | module Renderer 3 | # Renders an ItemContainer as a
element and its containing items as 4 | # elements. 5 | # It adds the 'selected' class to the element that is currently active. 6 | # 7 | # The Links renderer cannot be used to render nested navigations. If you 8 | # would like it to use with nested navigations, you have to render each 9 | # level separately. 10 | # 11 | # By default, the renderer sets the item's key as dom_id for the rendered 12 | # element unless the config option autogenerate_item_ids is set 13 | # to false. 14 | # The id can also be explicitely specified by setting the id in the 15 | # html-options of the 'item' method in the config/navigation.rb file. 16 | # The ItemContainer's dom_attributes are applied to the surrounding
17 | # element. 18 | class Links < SimpleNavigation::Renderer::Base 19 | def render(item_container) 20 | div_content = item_container.items 21 | .map { |item| tag_for(item) } 22 | .join(join_with) 23 | content_tag :div, div_content, item_container.dom_attributes 24 | end 25 | 26 | protected 27 | 28 | def join_with 29 | @join_with ||= options[:join_with] || '' 30 | end 31 | 32 | def options_for(item) 33 | { method: item.method }.merge(item.html_options) 34 | end 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /spec/simple_navigation/items_provider_spec.rb: -------------------------------------------------------------------------------- 1 | module SimpleNavigation 2 | describe ItemsProvider do 3 | let(:items_provider) { ItemsProvider.new(provider) } 4 | 5 | describe '#items' do 6 | let(:items) { double(:items) } 7 | 8 | context 'when provider is a symbol' do 9 | let(:context) { double(:context, provider_method: items) } 10 | let(:provider) { :provider_method } 11 | 12 | before { allow(SimpleNavigation).to receive_messages(context_for_eval: context) } 13 | 14 | it 'retrieves the items from the evaluation context' do 15 | expect(items_provider.items).to eq items 16 | end 17 | end 18 | 19 | context 'when provider responds to :items' do 20 | let(:provider) { double(:provider, items: items) } 21 | 22 | it 'retrieves the items from the provider object' do 23 | expect(items_provider.items).to eq items 24 | end 25 | end 26 | 27 | context 'provider is a collection' do 28 | let(:provider) { [] } 29 | 30 | it 'retrieves the items by returning the provider' do 31 | expect(items_provider.items).to eq provider 32 | end 33 | end 34 | 35 | context 'when provider is something else' do 36 | let(:provider) { double(:provider) } 37 | 38 | it 'raises an exception' do 39 | expect{ items_provider.items }.to raise_error(RuntimeError, /items_provider either must be a symbol .*, an object .* or an enumerable/) 40 | end 41 | end 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /lib/simple_navigation/adapters/sinatra.rb: -------------------------------------------------------------------------------- 1 | require 'cgi' 2 | 3 | module SimpleNavigation 4 | module Adapters 5 | class Sinatra < Base 6 | def self.register(app) 7 | SimpleNavigation.set_env(app.root, app.environment) 8 | end 9 | 10 | def initialize(context) 11 | @context = context 12 | @request = context.request 13 | end 14 | 15 | def context_for_eval 16 | context || fail('no context set for evaluation the config file') 17 | end 18 | 19 | def request_uri 20 | request.fullpath 21 | end 22 | 23 | def request_path 24 | request.path 25 | end 26 | 27 | def current_page?(url) 28 | url_string = CGI.unescape(url) 29 | uri = if url_string.index('?') 30 | request_uri 31 | else 32 | request_uri.split('?').first 33 | end 34 | 35 | if url_string =~ %r(^\w+://) 36 | uri = "#{request.scheme}://#{request.host_with_port}#{uri}" 37 | end 38 | 39 | url_string == CGI.unescape(uri) 40 | end 41 | 42 | def link_to(name, url, options = {}) 43 | "#{name}" 44 | end 45 | 46 | def content_tag(type, content, options = {}) 47 | "<#{type}#{to_attributes(options)}>#{content}" 48 | end 49 | 50 | protected 51 | 52 | def to_attributes(options) 53 | options.map { |k, v| v.nil? ? '' : " #{k}='#{v}'" }.join 54 | end 55 | end 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /lib/simple_navigation/renderer/list.rb: -------------------------------------------------------------------------------- 1 | module SimpleNavigation 2 | module Renderer 3 | # Renders an ItemContainer as a