jordanyeo-simple-navigation 3.11.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.
- data/CHANGELOG +265 -0
- data/Gemfile +17 -0
- data/README +22 -0
- data/Rakefile +47 -0
- data/VERSION +1 -0
- data/generators/navigation_config/USAGE +1 -0
- data/generators/navigation_config/navigation_config_generator.rb +8 -0
- data/generators/navigation_config/templates/config/navigation.rb +76 -0
- data/lib/generators/navigation_config/navigation_config_generator.rb +12 -0
- data/lib/simple-navigation.rb +1 -0
- data/lib/simple_navigation.rb +167 -0
- data/lib/simple_navigation/adapters.rb +10 -0
- data/lib/simple_navigation/adapters/base.rb +37 -0
- data/lib/simple_navigation/adapters/nanoc.rb +45 -0
- data/lib/simple_navigation/adapters/padrino.rb +20 -0
- data/lib/simple_navigation/adapters/rails.rb +93 -0
- data/lib/simple_navigation/adapters/sinatra.rb +69 -0
- data/lib/simple_navigation/core.rb +5 -0
- data/lib/simple_navigation/core/configuration.rb +72 -0
- data/lib/simple_navigation/core/item.rb +144 -0
- data/lib/simple_navigation/core/item_adapter.rb +63 -0
- data/lib/simple_navigation/core/item_container.rb +147 -0
- data/lib/simple_navigation/core/items_provider.rb +35 -0
- data/lib/simple_navigation/rails_controller_methods.rb +144 -0
- data/lib/simple_navigation/rendering.rb +12 -0
- data/lib/simple_navigation/rendering/helpers.rb +123 -0
- data/lib/simple_navigation/rendering/renderer/base.rb +107 -0
- data/lib/simple_navigation/rendering/renderer/breadcrumbs.rb +59 -0
- data/lib/simple_navigation/rendering/renderer/json.rb +29 -0
- data/lib/simple_navigation/rendering/renderer/links.rb +32 -0
- data/lib/simple_navigation/rendering/renderer/list.rb +29 -0
- data/lib/simple_navigation/rendering/renderer/text.rb +26 -0
- data/rails/init.rb +1 -0
- data/spec/lib/simple_navigation/adapters/padrino_spec.rb +31 -0
- data/spec/lib/simple_navigation/adapters/rails_spec.rb +287 -0
- data/spec/lib/simple_navigation/adapters/sinatra_spec.rb +80 -0
- data/spec/lib/simple_navigation/core/configuration_spec.rb +128 -0
- data/spec/lib/simple_navigation/core/item_adapter_spec.rb +212 -0
- data/spec/lib/simple_navigation/core/item_container_spec.rb +451 -0
- data/spec/lib/simple_navigation/core/item_spec.rb +566 -0
- data/spec/lib/simple_navigation/core/items_provider_spec.rb +60 -0
- data/spec/lib/simple_navigation/rails_controller_methods_spec.rb +249 -0
- data/spec/lib/simple_navigation/rendering/helpers_spec.rb +276 -0
- data/spec/lib/simple_navigation/rendering/renderer/base_spec.rb +199 -0
- data/spec/lib/simple_navigation/rendering/renderer/breadcrumbs_spec.rb +101 -0
- data/spec/lib/simple_navigation/rendering/renderer/json_spec.rb +48 -0
- data/spec/lib/simple_navigation/rendering/renderer/links_spec.rb +64 -0
- data/spec/lib/simple_navigation/rendering/renderer/list_spec.rb +211 -0
- data/spec/lib/simple_navigation/rendering/renderer/text_spec.rb +41 -0
- data/spec/lib/simple_navigation_spec.rb +307 -0
- data/spec/spec_helper.rb +108 -0
- metadata +199 -0
@@ -0,0 +1,12 @@
|
|
1
|
+
class NavigationConfigGenerator < Rails::Generators::Base
|
2
|
+
def self.source_root
|
3
|
+
@source_root ||= File.expand_path(File.join(File.dirname(__FILE__),'..','..','..','generators','navigation_config', 'templates'))
|
4
|
+
end
|
5
|
+
|
6
|
+
desc 'Creates a template config file for the simple-navigation plugin. You will find the generated file in config/navigation.rb.'
|
7
|
+
def navigation_config
|
8
|
+
copy_file('config/navigation.rb', 'config/navigation.rb')
|
9
|
+
say File.read(File.expand_path(File.join(File.dirname(__FILE__),'..','..','..','README')))
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'simple_navigation'
|
@@ -0,0 +1,167 @@
|
|
1
|
+
# cherry picking active_support stuff
|
2
|
+
require 'active_support/core_ext/array'
|
3
|
+
require 'active_support/core_ext/hash'
|
4
|
+
require 'active_support/core_ext/module/attribute_accessors'
|
5
|
+
|
6
|
+
require 'simple_navigation/core'
|
7
|
+
require 'simple_navigation/rendering'
|
8
|
+
require 'simple_navigation/adapters'
|
9
|
+
|
10
|
+
require 'forwardable'
|
11
|
+
|
12
|
+
# A plugin for generating a simple navigation. See README for resources on usage instructions.
|
13
|
+
module SimpleNavigation
|
14
|
+
|
15
|
+
mattr_accessor :adapter_class, :adapter, :config_files, :config_file_paths, :default_renderer, :registered_renderers, :root, :environment
|
16
|
+
|
17
|
+
# Cache for loaded config files
|
18
|
+
self.config_files = {}
|
19
|
+
|
20
|
+
# Allows for multiple config_file_paths. Needed if a plugin itself uses simple-navigation and therefore has its own config file
|
21
|
+
self.config_file_paths = []
|
22
|
+
|
23
|
+
# Maps renderer keys to classes. The keys serve as shortcut in the render_navigation calls (:renderer => :list)
|
24
|
+
self.registered_renderers = {
|
25
|
+
:list => SimpleNavigation::Renderer::List,
|
26
|
+
:links => SimpleNavigation::Renderer::Links,
|
27
|
+
:breadcrumbs => SimpleNavigation::Renderer::Breadcrumbs,
|
28
|
+
:text => SimpleNavigation::Renderer::Text,
|
29
|
+
:json => SimpleNavigation::Renderer::Json
|
30
|
+
}
|
31
|
+
|
32
|
+
class << self
|
33
|
+
extend Forwardable
|
34
|
+
|
35
|
+
def_delegators :adapter, :request, :request_uri, :request_path, :context_for_eval, :current_page?
|
36
|
+
def_delegators :adapter_class, :register
|
37
|
+
|
38
|
+
# Sets the root path and current environment as specified. Also sets the default config_file_path.
|
39
|
+
def set_env(root, environment)
|
40
|
+
self.root = root
|
41
|
+
self.environment = environment
|
42
|
+
self.config_file_paths << SimpleNavigation.default_config_file_path
|
43
|
+
end
|
44
|
+
|
45
|
+
# Returns the current framework in which the plugin is running.
|
46
|
+
def framework
|
47
|
+
return :rails if defined?(Rails)
|
48
|
+
return :padrino if defined?(Padrino)
|
49
|
+
return :sinatra if defined?(Sinatra)
|
50
|
+
return :nanoc if defined?(Nanoc3)
|
51
|
+
raise 'simple_navigation currently only works for Rails, Sinatra and Padrino apps'
|
52
|
+
end
|
53
|
+
|
54
|
+
# Loads the adapter for the current framework
|
55
|
+
def load_adapter
|
56
|
+
self.adapter_class = case framework
|
57
|
+
when :rails
|
58
|
+
SimpleNavigation::Adapters::Rails
|
59
|
+
when :sinatra
|
60
|
+
SimpleNavigation::Adapters::Sinatra
|
61
|
+
when :padrino
|
62
|
+
SimpleNavigation::Adapters::Padrino
|
63
|
+
when :nanoc
|
64
|
+
SimpleNavigation::Adapters::Nanoc
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Creates a new adapter instance based on the context in which render_navigation has been called.
|
69
|
+
def init_adapter_from(context)
|
70
|
+
self.adapter = self.adapter_class.new(context)
|
71
|
+
end
|
72
|
+
|
73
|
+
def default_config_file_path
|
74
|
+
File.join(SimpleNavigation.root, 'config')
|
75
|
+
end
|
76
|
+
|
77
|
+
# Returns true if the config_file for specified context does exist.
|
78
|
+
def config_file?(navigation_context = :default)
|
79
|
+
!!config_file(navigation_context)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Returns the path to the config file for the given navigation context or nil if no matching config file can be found.
|
83
|
+
# If multiple config_paths are set, it returns the first matching path.
|
84
|
+
def config_file(navigation_context = :default)
|
85
|
+
config_file_paths.collect { |path| File.join(path, config_file_name(navigation_context)) }.detect {|full_path| File.exists?(full_path)}
|
86
|
+
end
|
87
|
+
|
88
|
+
# Returns the name of the config file for the given navigation_context
|
89
|
+
def config_file_name(navigation_context = :default)
|
90
|
+
prefix = navigation_context == :default ? '' : "#{navigation_context.to_s.underscore}_"
|
91
|
+
"#{prefix}navigation.rb"
|
92
|
+
end
|
93
|
+
|
94
|
+
# Resets the list of config_file_paths to the specified path
|
95
|
+
def config_file_path=(path)
|
96
|
+
self.config_file_paths = [path]
|
97
|
+
end
|
98
|
+
|
99
|
+
# Reads the config_file for the specified navigation_context and stores it for later evaluation.
|
100
|
+
def load_config(navigation_context = :default)
|
101
|
+
raise "Config file '#{config_file_name(navigation_context)}' not found in path(s) #{config_file_paths.join(', ')}!" unless config_file?(navigation_context)
|
102
|
+
if self.environment == 'production'
|
103
|
+
self.config_files[navigation_context] ||= IO.read(config_file(navigation_context))
|
104
|
+
else
|
105
|
+
self.config_files[navigation_context] = IO.read(config_file(navigation_context))
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# Returns the singleton instance of the SimpleNavigation::Configuration
|
110
|
+
def config
|
111
|
+
SimpleNavigation::Configuration.instance
|
112
|
+
end
|
113
|
+
|
114
|
+
# Returns the ItemContainer that contains the items for the primary navigation
|
115
|
+
def primary_navigation
|
116
|
+
config.primary_navigation
|
117
|
+
end
|
118
|
+
|
119
|
+
# Returns the active item container for the specified level. Valid levels are
|
120
|
+
# * :all - in this case the primary_navigation is returned.
|
121
|
+
# * :leaves - the 'deepest' active item_container will be returned
|
122
|
+
# * a specific level - the active item_container for the specified level will be returned
|
123
|
+
# * a range of levels - the active item_container for the range's minimum will be returned
|
124
|
+
#
|
125
|
+
# Returns nil if there is no active item_container for the specified level.
|
126
|
+
def active_item_container_for(level)
|
127
|
+
case level
|
128
|
+
when :all
|
129
|
+
self.primary_navigation
|
130
|
+
when :leaves
|
131
|
+
self.primary_navigation.active_leaf_container
|
132
|
+
when Integer
|
133
|
+
self.primary_navigation.active_item_container_for(level)
|
134
|
+
when Range
|
135
|
+
self.primary_navigation.active_item_container_for(level.min)
|
136
|
+
else
|
137
|
+
raise ArgumentError, "Invalid navigation level: #{level}"
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# Registers a renderer.
|
142
|
+
#
|
143
|
+
# === Example
|
144
|
+
# To register your own renderer:
|
145
|
+
#
|
146
|
+
# SimpleNavigation.register_renderer :my_renderer => My::RendererClass
|
147
|
+
#
|
148
|
+
# Then in the view you can call:
|
149
|
+
#
|
150
|
+
# render_navigation(:renderer => :my_renderer)
|
151
|
+
def register_renderer(renderer_hash)
|
152
|
+
self.registered_renderers.merge!(renderer_hash)
|
153
|
+
end
|
154
|
+
|
155
|
+
private
|
156
|
+
|
157
|
+
def apply_defaults(options)
|
158
|
+
options[:level] = options.delete(:levels) if options[:levels]
|
159
|
+
{:context => :default, :level => :all}.merge(options)
|
160
|
+
end
|
161
|
+
|
162
|
+
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
166
|
+
|
167
|
+
SimpleNavigation.load_adapter
|
@@ -0,0 +1,10 @@
|
|
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
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module SimpleNavigation
|
2
|
+
module Adapters
|
3
|
+
|
4
|
+
# This is the base class for all adapters. 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 SimpleNavigation::Helpers where appropriate.
|
12
|
+
def self.register; end
|
13
|
+
|
14
|
+
# Returns the full path incl. query params
|
15
|
+
def request_uri; end
|
16
|
+
|
17
|
+
# Returns the path without query params
|
18
|
+
def request_path; end
|
19
|
+
|
20
|
+
# Returns the context in which the config files will be evaluated
|
21
|
+
def context_for_eval; end
|
22
|
+
|
23
|
+
# Returns true if the current request's url matches the specified url.
|
24
|
+
# Used to determine if an item should be autohighlighted.
|
25
|
+
def current_page?(url); end
|
26
|
+
|
27
|
+
# Returns a link with the specified name, url and options.
|
28
|
+
# Used for rendering.
|
29
|
+
def link_to(name, url, options={}); end
|
30
|
+
|
31
|
+
# Returns a tag of the specified type, content and options.
|
32
|
+
# Used for rendering.
|
33
|
+
def content_tag(type, content, options={}); end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,45 @@
|
|
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
|
+
"<a href='#{url}' #{to_attributes(options)}>#{name}</a>"
|
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}</#{type}>"
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
def to_attributes(options)
|
41
|
+
options.map {|k, v| v.nil? ? nil : "#{k}='#{v}'"}.compact.join(' ')
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module SimpleNavigation
|
2
|
+
module Adapters
|
3
|
+
class Padrino < Sinatra
|
4
|
+
|
5
|
+
def self.register
|
6
|
+
SimpleNavigation.set_env(PADRINO_ROOT, PADRINO_ENV)
|
7
|
+
::Padrino::Application.send(:helpers, SimpleNavigation::Helpers)
|
8
|
+
end
|
9
|
+
|
10
|
+
def link_to(name, url, options={})
|
11
|
+
context.link_to(name, url, options)
|
12
|
+
end
|
13
|
+
|
14
|
+
def content_tag(type, content, options={})
|
15
|
+
context.content_tag(type, content.html_safe, options)
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module SimpleNavigation
|
2
|
+
module Adapters
|
3
|
+
class Rails < Base
|
4
|
+
|
5
|
+
attr_reader :controller, :template
|
6
|
+
|
7
|
+
def self.register
|
8
|
+
SimpleNavigation.set_env(rails_root, rails_env)
|
9
|
+
ActionController::Base.send(:include, SimpleNavigation::Helpers)
|
10
|
+
SimpleNavigation::Helpers.instance_methods.each do |m|
|
11
|
+
ActionController::Base.send(:helper_method, m.to_sym)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(context)
|
16
|
+
@controller = extract_controller_from context
|
17
|
+
@template = template_from @controller
|
18
|
+
@request = @template.request if @template
|
19
|
+
end
|
20
|
+
|
21
|
+
def request_uri
|
22
|
+
return '' unless request
|
23
|
+
return request.fullpath if request.respond_to?(:fullpath)
|
24
|
+
request.request_uri
|
25
|
+
end
|
26
|
+
|
27
|
+
def request_path
|
28
|
+
return '' unless request
|
29
|
+
request.path
|
30
|
+
end
|
31
|
+
|
32
|
+
def context_for_eval
|
33
|
+
raise 'no context set for evaluation the config file' unless template || controller
|
34
|
+
template || controller
|
35
|
+
end
|
36
|
+
|
37
|
+
def current_page?(url)
|
38
|
+
template.current_page?(url) if template
|
39
|
+
end
|
40
|
+
|
41
|
+
def link_to(name, url, options={})
|
42
|
+
template.link_to(html_safe(name), url, options) if template
|
43
|
+
end
|
44
|
+
|
45
|
+
def content_tag(type, content, options={})
|
46
|
+
template.content_tag(type, html_safe(content), options) if template
|
47
|
+
end
|
48
|
+
|
49
|
+
protected
|
50
|
+
|
51
|
+
def self.rails_root
|
52
|
+
gte_rails3? ? ::Rails.root : ::RAILS_ROOT
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.rails_env
|
56
|
+
gte_rails3? ? ::Rails.env : ::RAILS_ENV
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.gte_rails3?
|
60
|
+
::Rails::VERSION::MAJOR >= 3
|
61
|
+
end
|
62
|
+
|
63
|
+
def template_from(controller)
|
64
|
+
controller.respond_to?(:view_context) ? controller.view_context : controller.instance_variable_get(:@template)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Marks the specified input as html_safe (for Rails3). Does nothing if html_safe is not defined on input.
|
68
|
+
#
|
69
|
+
def html_safe(input)
|
70
|
+
input.respond_to?(:html_safe) ? input.html_safe : input
|
71
|
+
end
|
72
|
+
|
73
|
+
# Extracts a controller from the context.
|
74
|
+
def extract_controller_from(context)
|
75
|
+
context.respond_to?(:controller) ?
|
76
|
+
context.controller || context :
|
77
|
+
context
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Initializer for Rails3
|
85
|
+
if defined?(Rails) && SimpleNavigation::Adapters::Rails.gte_rails3?
|
86
|
+
module SimpleNavigation
|
87
|
+
class Railtie < Rails::Railtie
|
88
|
+
initializer "simple_navigation.register" do |app|
|
89
|
+
SimpleNavigation.register
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
|
3
|
+
module SimpleNavigation
|
4
|
+
module Adapters
|
5
|
+
class Sinatra < Base
|
6
|
+
|
7
|
+
def self.register
|
8
|
+
SimpleNavigation.set_env(sinatra_root, sinatra_environment)
|
9
|
+
::Sinatra::Application.send(:helpers, SimpleNavigation::Helpers)
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(context)
|
13
|
+
@context = context
|
14
|
+
@request = context.request
|
15
|
+
end
|
16
|
+
|
17
|
+
def context_for_eval
|
18
|
+
raise 'no context set for evaluation the config file' unless context
|
19
|
+
context
|
20
|
+
end
|
21
|
+
|
22
|
+
def request_uri
|
23
|
+
request.fullpath
|
24
|
+
end
|
25
|
+
|
26
|
+
def request_path
|
27
|
+
request.path
|
28
|
+
end
|
29
|
+
|
30
|
+
def current_page?(url)
|
31
|
+
url_string = CGI.unescape(url)
|
32
|
+
if url_string.index("?")
|
33
|
+
uri = request_uri
|
34
|
+
else
|
35
|
+
uri = request_uri.split('?').first
|
36
|
+
end
|
37
|
+
uri = CGI.unescape(uri)
|
38
|
+
if url_string =~ /^\w+:\/\//
|
39
|
+
url_string == "#{request.scheme}://#{request.host_with_port}#{uri}"
|
40
|
+
else
|
41
|
+
url_string == uri
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def link_to(name, url, options={})
|
46
|
+
"<a href='#{url}'#{to_attributes(options)}>#{name}</a>"
|
47
|
+
end
|
48
|
+
|
49
|
+
def content_tag(type, content, options={})
|
50
|
+
"<#{type}#{to_attributes(options)}>#{content}</#{type}>"
|
51
|
+
end
|
52
|
+
|
53
|
+
protected
|
54
|
+
|
55
|
+
def self.sinatra_root
|
56
|
+
::Sinatra::Application.root
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.sinatra_environment
|
60
|
+
::Sinatra::Application.environment
|
61
|
+
end
|
62
|
+
|
63
|
+
def to_attributes(options)
|
64
|
+
options.map {|k, v| v.nil? ? '' : " #{k}='#{v}'"}.join
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|