navigasmic 0.5.6 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +7 -1
- data/lib/generators/navigasmic/install/POST_INSTALL +6 -0
- data/lib/generators/navigasmic/install/install_generator.rb +17 -0
- data/lib/generators/navigasmic/install/templates/initializer.rb +145 -0
- data/lib/navigasmic.rb +5 -146
- data/lib/navigasmic/builders/crumb_builder.rb +20 -0
- data/lib/navigasmic/builders/list_builder.rb +90 -0
- data/lib/navigasmic/builders/map_builder.rb +77 -0
- data/lib/navigasmic/core/builders.rb +109 -0
- data/lib/navigasmic/core/configuration.rb +38 -0
- data/lib/navigasmic/core/item.rb +66 -0
- data/lib/navigasmic/rails.rb +2 -0
- data/lib/navigasmic/rails/engine.rb +9 -0
- data/lib/navigasmic/rails/view_helpers.rb +18 -0
- data/lib/navigasmic/version.rb +3 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/controllers/application_controller.rb +13 -0
- data/spec/dummy/app/controllers/blog/links_controller.rb +7 -0
- data/spec/dummy/app/controllers/blog/posts_controller.rb +7 -0
- data/spec/dummy/app/views/application/welcome.html.erb +4 -0
- data/spec/dummy/app/views/application/welcome.xml.builder +2 -0
- data/spec/dummy/app/views/blog/links/index.html +4 -0
- data/spec/dummy/app/views/blog/links/show.html +4 -0
- data/spec/dummy/app/views/blog/posts/index.html +4 -0
- data/spec/dummy/app/views/blog/posts/show.html +4 -0
- data/spec/dummy/app/views/layouts/_navigation.html.erb +73 -0
- data/spec/dummy/app/views/layouts/application.html.erb +40 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +57 -0
- data/spec/dummy/config/boot.rb +9 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +37 -0
- data/spec/dummy/config/environments/test.rb +37 -0
- data/spec/dummy/config/evergreen.rb +47 -0
- data/spec/dummy/config/initializers/additional_navigasmic.rb +14 -0
- data/spec/dummy/config/initializers/navigasmic.rb +145 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +68 -0
- data/spec/dummy/log/.gitkeep +0 -0
- data/spec/dummy/public/bootstrap.min.css +9 -0
- data/spec/dummy/public/bootstrap.min.js +6 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/public/jquery.min.js +2 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/spec_helper.rb +39 -0
- metadata +139 -63
- data/.document +0 -5
- data/README.textile +0 -276
- data/Rakefile +0 -57
- data/VERSION +0 -1
- data/lib/builders/html_builder.rb +0 -69
- data/lib/builders/xml_builder.rb +0 -51
- data/navigasmic.gemspec +0 -49
- data/test/navigasmic_test.rb +0 -7
- data/test/test_helper.rb +0 -10
@@ -0,0 +1,77 @@
|
|
1
|
+
module Navigasmic::Builder
|
2
|
+
class MapBuilder < Base
|
3
|
+
class Configuration < Base::Configuration
|
4
|
+
|
5
|
+
attr_accessor :option_namespace
|
6
|
+
attr_accessor :wrapper_tag, :group_tag, :item_tag, :label_generator
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
# where you want the changefreq and other options to be looked for
|
10
|
+
@option_namespace = :map
|
11
|
+
|
12
|
+
# tag configurations
|
13
|
+
@wrapper_tag = :urlset
|
14
|
+
@item_tag = :url
|
15
|
+
|
16
|
+
super
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(context, name, options, &block)
|
21
|
+
super
|
22
|
+
@options['xmlns'] ||= 'http://www.sitemaps.org/schemas/sitemap/0.9'
|
23
|
+
@options['xmlns:xsi'] ||= 'http://www.w3.org/2001/XMLSchema-instance'
|
24
|
+
@options['xsi:schemaLocation'] ||= 'http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd'
|
25
|
+
@options[:changefreq] ||= 'yearly'
|
26
|
+
end
|
27
|
+
|
28
|
+
def render
|
29
|
+
content_tag(@config.wrapper_tag, capture(&@definition), @options)
|
30
|
+
end
|
31
|
+
|
32
|
+
def group(label = nil, options = {}, &block)
|
33
|
+
raise ArgumentError, "Missing block for group" unless block_given?
|
34
|
+
return '' unless visible?(options)
|
35
|
+
|
36
|
+
concat(capture(&block))
|
37
|
+
end
|
38
|
+
|
39
|
+
def item(label, *args, &block)
|
40
|
+
options = args.extract_options!
|
41
|
+
options = flatten_and_eval_options(options)
|
42
|
+
return '' unless visible?(options)
|
43
|
+
|
44
|
+
item = Navigasmic::Item.new(self, label, extract_and_determine_link(label, options, *args), options)
|
45
|
+
|
46
|
+
concat(capture(&block)) if block_given?
|
47
|
+
return '' unless item.link?
|
48
|
+
|
49
|
+
concat(structure_for(label, item.link, options))
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def structure_for(label, link, options, &block)
|
55
|
+
content = content_tag(:loc, link_for(link, options))
|
56
|
+
content << content_tag(:name, label)
|
57
|
+
if opts = options.delete(@config.option_namespace)
|
58
|
+
content << content_tag(:changefreq, opts[:changefreq] || 'yearly')
|
59
|
+
content << content_tag(:lastmod, opts[:lastmod]) if opts.has_key?(:lastmod)
|
60
|
+
content << content_tag(:priority, opt[:priority]) if opts.has_key?(:priority)
|
61
|
+
end
|
62
|
+
|
63
|
+
content_tag(@config.item_tag, content.html_safe)
|
64
|
+
end
|
65
|
+
|
66
|
+
def link_for(link, options)
|
67
|
+
host = options.delete(:host) || @context.request.host
|
68
|
+
if link.is_a?(Hash)
|
69
|
+
link[:host] ||= host
|
70
|
+
else
|
71
|
+
link = "#{@context.request.protocol}#{host}#{@context.request.port}#{link}"
|
72
|
+
end
|
73
|
+
url_for(link)
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
module Navigasmic::Builder
|
2
|
+
autoload :ListBuilder, 'navigasmic/builders/list_builder'
|
3
|
+
autoload :MapBuilder, 'navigasmic/builders/map_builder'
|
4
|
+
autoload :CrumbBuilder, 'navigasmic/builders/crumb_builder'
|
5
|
+
|
6
|
+
class Base
|
7
|
+
class Configuration
|
8
|
+
attr_accessor :excluded_keys
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@excluded_keys ||= []
|
12
|
+
yield self if block_given?
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(context, name, options, &block)
|
17
|
+
@definition = block_given? ? block : Navigasmic.configuration.definitions[name]
|
18
|
+
raise ArgumentError, "Missing block or configuration" unless @definition
|
19
|
+
|
20
|
+
@context, @name, @options = context, name, options
|
21
|
+
@config = configuration_or_default
|
22
|
+
remove_excluded_options(@options)
|
23
|
+
end
|
24
|
+
|
25
|
+
def group(label = nil, options = {}, &block)
|
26
|
+
raise "Expected subclass to implement group"
|
27
|
+
end
|
28
|
+
|
29
|
+
def item(label, *args, &block)
|
30
|
+
raise "Expected subclass to implement item"
|
31
|
+
end
|
32
|
+
|
33
|
+
def render
|
34
|
+
raise "Expected subclass to implement render"
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def configuration_or_default
|
40
|
+
configurations = Navigasmic.configuration.builder_configurations[self.class.to_s]
|
41
|
+
proc = configurations.present? ? configurations[@options.delete(:config) || :default] : nil
|
42
|
+
self.class::Configuration.new(&proc)
|
43
|
+
end
|
44
|
+
|
45
|
+
def remove_excluded_options(options)
|
46
|
+
@config.excluded_keys.each { |key| options.delete(key) }
|
47
|
+
end
|
48
|
+
|
49
|
+
def capture(&block)
|
50
|
+
(block_given? ? @context.capture(self, &block) : nil).to_s.html_safe
|
51
|
+
end
|
52
|
+
|
53
|
+
def eval_in_context(&block)
|
54
|
+
@context.instance_eval(&block)
|
55
|
+
end
|
56
|
+
|
57
|
+
def method_missing(meth, *args, &block)
|
58
|
+
@context.send(meth, *args, &block)
|
59
|
+
end
|
60
|
+
|
61
|
+
def flatten_and_eval_options(options)
|
62
|
+
remove_excluded_options(options)
|
63
|
+
options.inject({}) do |hash, (key, value)|
|
64
|
+
if value.is_a?(Array)
|
65
|
+
value = value.map{ |v| v.is_a?(Proc) ? eval_in_context(&v) : v }
|
66
|
+
elsif value.is_a?(Proc)
|
67
|
+
value = eval_in_context(&value)
|
68
|
+
end
|
69
|
+
hash.update(key => value)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def visible?(options)
|
74
|
+
if options[:hidden_unless].nil?
|
75
|
+
true
|
76
|
+
elsif options[:hidden_unless].is_a?(Proc)
|
77
|
+
eval_in_context(&options[:hidden_unless])
|
78
|
+
else
|
79
|
+
options[:hidden_unless]
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def extract_and_determine_link(label, options, *args)
|
84
|
+
determine_link(label, extract_link(options, *args))
|
85
|
+
end
|
86
|
+
|
87
|
+
def extract_link(options, *args)
|
88
|
+
if args.length == 1
|
89
|
+
args.delete_at(0)
|
90
|
+
else
|
91
|
+
hash = {controller: options.delete(:controller), action: options.delete(:action)}
|
92
|
+
hash = options.delete(:link) || hash
|
93
|
+
hash.select{ |key, value| value.present? }
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def determine_link(label, link)
|
98
|
+
if link.blank?
|
99
|
+
path_helper = "#{label.to_s.underscore.gsub(/\s/, '_')}_path"
|
100
|
+
@context.send(path_helper) if @context.respond_to?(path_helper)
|
101
|
+
elsif link.is_a?(Proc)
|
102
|
+
eval_in_context(&link)
|
103
|
+
else
|
104
|
+
link
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
module Navigasmic
|
4
|
+
class Configuration
|
5
|
+
include Singleton
|
6
|
+
|
7
|
+
cattr_accessor :default_builder
|
8
|
+
@@default_builder = Navigasmic::Builder::ListBuilder
|
9
|
+
|
10
|
+
cattr_accessor :builder_configurations
|
11
|
+
@@builder_configurations = {}
|
12
|
+
|
13
|
+
cattr_accessor :definitions
|
14
|
+
@@definitions = {}
|
15
|
+
|
16
|
+
def self.semantic_navigation(name, &block)
|
17
|
+
@@definitions[name] = block
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.builder(builder, &block)
|
21
|
+
if builder.is_a?(Hash)
|
22
|
+
name = builder.keys[0]
|
23
|
+
builder = builder[name]
|
24
|
+
else
|
25
|
+
name = :default
|
26
|
+
end
|
27
|
+
@@builder_configurations[builder.to_s] ||= {}
|
28
|
+
@@builder_configurations[builder.to_s][name] = block
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
mattr_accessor :configuration
|
33
|
+
@@configuration = Configuration
|
34
|
+
|
35
|
+
def self.setup
|
36
|
+
yield @@configuration
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
class Navigasmic::Item
|
2
|
+
|
3
|
+
attr_accessor :link
|
4
|
+
def initialize(builder, label, link, options = {})
|
5
|
+
@label, @link = label, link
|
6
|
+
@disabled = options.delete(:disabled_if)
|
7
|
+
@visible = builder.send(:visible?, options)
|
8
|
+
options.delete(:hidden_unless)
|
9
|
+
|
10
|
+
highlighting_from(options.delete(:highlights_on))
|
11
|
+
end
|
12
|
+
|
13
|
+
def hidden?
|
14
|
+
!@visible
|
15
|
+
end
|
16
|
+
|
17
|
+
def disabled?
|
18
|
+
@disabled
|
19
|
+
end
|
20
|
+
|
21
|
+
def link?
|
22
|
+
@link.present? && !disabled?
|
23
|
+
end
|
24
|
+
|
25
|
+
def highlights_on?(path, params)
|
26
|
+
params = clean_unwanted_keys(params)
|
27
|
+
result = false
|
28
|
+
|
29
|
+
@highlights_on.each do |highlight|
|
30
|
+
highlighted = true
|
31
|
+
|
32
|
+
case highlight
|
33
|
+
when String then highlighted &= path == highlight
|
34
|
+
when Regexp then highlighted &= path.match(highlight)
|
35
|
+
when TrueClass then highlighted &= highlight
|
36
|
+
when FalseClass then highlighted &= highlight
|
37
|
+
when Hash
|
38
|
+
clean_unwanted_keys(highlight).each do |key, value|
|
39
|
+
value.gsub!(/^\//, '') if key == :controller
|
40
|
+
highlighted &= value == params[key].to_s
|
41
|
+
end
|
42
|
+
else raise 'highlighting rules should be an array containing any of/or a Boolean, String, Regexp, Hash or Proc'
|
43
|
+
end
|
44
|
+
|
45
|
+
result |= highlighted
|
46
|
+
end
|
47
|
+
|
48
|
+
result
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def highlighting_from(rules)
|
54
|
+
@highlights_on = []
|
55
|
+
@highlights_on << @link if link?
|
56
|
+
|
57
|
+
return if rules.blank?
|
58
|
+
@highlights_on += rules.kind_of?(Array) ? rules : [rules]
|
59
|
+
end
|
60
|
+
|
61
|
+
def clean_unwanted_keys(hash)
|
62
|
+
ignored_keys = [:only_path, :use_route]
|
63
|
+
hash.dup.delete_if { |key, value| ignored_keys.include?(key) }
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Navigasmic::ViewHelpers
|
2
|
+
|
3
|
+
# Semantic navigation view helper method
|
4
|
+
#
|
5
|
+
# Example Usage:
|
6
|
+
#
|
7
|
+
# <%= semantic_navigation :primary, class: 'primary-nav', builder: MyCustomBuilder do |n| %>
|
8
|
+
# <% n.group 'My Thoughts' do %>
|
9
|
+
# <% n.item 'Blog Posts', controller: 'posts', class: 'featured', id: 'blog_posts' %>
|
10
|
+
# <% end %>
|
11
|
+
# <% end %>
|
12
|
+
def semantic_navigation(name, options = {}, &block)
|
13
|
+
|
14
|
+
builder = options.delete(:builder) || Navigasmic.configuration.default_builder
|
15
|
+
builder.new(self, name, options, &block).render
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
data/spec/dummy/Rakefile
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
3
|
+
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
|
4
|
+
|
5
|
+
require File.expand_path('../config/application', __FILE__)
|
6
|
+
|
7
|
+
Dummy::Application.load_tasks
|
@@ -0,0 +1,73 @@
|
|
1
|
+
<h3>Rendering navigation defined in configuration</h3>
|
2
|
+
<%= semantic_navigation :primary, config: :blockquote %>
|
3
|
+
|
4
|
+
<h3>Rendering navigation defined in view</h3>
|
5
|
+
<%= semantic_navigation :primary do |n| %>
|
6
|
+
<!-- Adding custom markup in the view -->
|
7
|
+
<li>Custom node</li>
|
8
|
+
|
9
|
+
<!-- Various examples of group method -->
|
10
|
+
<% n.group 'Group' do %>
|
11
|
+
<li>Custom node - should be rendered</li>
|
12
|
+
<% end %>
|
13
|
+
<% n.group 'Group with various attributes', data: {attr: true}, class: 'foo' do %>
|
14
|
+
Custom content - should be rendered
|
15
|
+
<% end %>
|
16
|
+
<% n.group 'Nested groups' do %>
|
17
|
+
<% n.group 'Level 1' do %>
|
18
|
+
<% n.group 'Level 2' do %>
|
19
|
+
Custom content - should be rendered
|
20
|
+
<% end %>
|
21
|
+
<% end %>
|
22
|
+
<% end %>
|
23
|
+
|
24
|
+
<!-- Using hidden_unless for groups -->
|
25
|
+
<% n.group 'Hidden unless: true', class: 'foo', hidden_unless: true do %>
|
26
|
+
Custom content - should be rendered
|
27
|
+
<% end %>
|
28
|
+
<% n.group 'Hidden unless: false', hidden_unless: false do %>
|
29
|
+
Custom content - shouldn't be rendered
|
30
|
+
<% end %>
|
31
|
+
<% n.group 'Hidden unless: proc{ false }', hidden_unless: proc{ false } do %>
|
32
|
+
Custom content - shouldn't be rendered
|
33
|
+
<% end %>
|
34
|
+
|
35
|
+
<% n.group 'Various item examples within a group' do %>
|
36
|
+
<% n.item 'Using a string for href and various attributes', '/my_awesome_blog', data: {attr: true}, class: 'foo' %>
|
37
|
+
<% n.item 'Providing the controller in options', controller: '/blog/posts', class: 'foo' %>
|
38
|
+
<% n.item 'Providing the controller in the link option', link: {controller: '/blog/posts'} %>
|
39
|
+
|
40
|
+
<!-- Only providing the label, which should auto-link to the images controller -->
|
41
|
+
<% n.item 'My Awesome Blog' %>
|
42
|
+
<% end %>
|
43
|
+
|
44
|
+
<% n.group 'Highlighting under various situations' do %>
|
45
|
+
<% n.item 'When the highlight param is set', highlights_on: params[:highlight].present? %>
|
46
|
+
<% n.item 'Always (using an array)', highlights_on: [true, false, false] %>
|
47
|
+
<% n.item 'When the highlight param is set (using a proc)', highlights_on: proc{ params[:highlight].present? } %>
|
48
|
+
<% n.item 'When the highlight param is set (using a proc in an array)', highlights_on: [proc{ params[:highlight].present? }] %>
|
49
|
+
<% n.item 'When on any path beginning with "my_aw" (using regexp)', highlights_on: [/^\/my_aw/] %>
|
50
|
+
<% n.item 'When on "/my_awesome_blog" (using string)', highlights_on: '/my_awesome_blog' %>
|
51
|
+
<% n.item 'When on any action in the application controller', highlights_on: {controller: 'application'} %>
|
52
|
+
<% end %>
|
53
|
+
|
54
|
+
<!-- Examples of disabling items -->
|
55
|
+
<% n.item 'Disabled if: false', disabled_if: false %>
|
56
|
+
<% n.item 'Disabled if: true', disabled_if: true %>
|
57
|
+
<% n.item 'Disabled if: proc{ true }', disabled_if: proc{ true } %>
|
58
|
+
|
59
|
+
<!-- Examples of hiding items -->
|
60
|
+
<% n.item 'Hidden unless: false', hidden_unless: false %>
|
61
|
+
<% n.item 'Hidden unless: true', hidden_unless: true %>
|
62
|
+
<% n.item 'Hidden unless: proc{ true }', hidden_unless: proc{ true } %>
|
63
|
+
|
64
|
+
<% n.item t("hello"), '/foo' %>
|
65
|
+
|
66
|
+
<% n.item 'Nested items', '/blog/posts', disabled_if: false do %>
|
67
|
+
<% n.item 'Level 1', '/my_awesome_blog' do %>
|
68
|
+
<% n.item 'Level 2', '/foo', disabled_if: true do %>
|
69
|
+
<li>Custom node</li>
|
70
|
+
<% end %>
|
71
|
+
<% end %>
|
72
|
+
<% end %>
|
73
|
+
<% end %>
|