navigasmic 0.5.6 → 1.0.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/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 %>
|