navigasmic 0.5.6 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. data/LICENSE +7 -1
  2. data/lib/generators/navigasmic/install/POST_INSTALL +6 -0
  3. data/lib/generators/navigasmic/install/install_generator.rb +17 -0
  4. data/lib/generators/navigasmic/install/templates/initializer.rb +145 -0
  5. data/lib/navigasmic.rb +5 -146
  6. data/lib/navigasmic/builders/crumb_builder.rb +20 -0
  7. data/lib/navigasmic/builders/list_builder.rb +90 -0
  8. data/lib/navigasmic/builders/map_builder.rb +77 -0
  9. data/lib/navigasmic/core/builders.rb +109 -0
  10. data/lib/navigasmic/core/configuration.rb +38 -0
  11. data/lib/navigasmic/core/item.rb +66 -0
  12. data/lib/navigasmic/rails.rb +2 -0
  13. data/lib/navigasmic/rails/engine.rb +9 -0
  14. data/lib/navigasmic/rails/view_helpers.rb +18 -0
  15. data/lib/navigasmic/version.rb +3 -0
  16. data/spec/dummy/Rakefile +7 -0
  17. data/spec/dummy/app/controllers/application_controller.rb +13 -0
  18. data/spec/dummy/app/controllers/blog/links_controller.rb +7 -0
  19. data/spec/dummy/app/controllers/blog/posts_controller.rb +7 -0
  20. data/spec/dummy/app/views/application/welcome.html.erb +4 -0
  21. data/spec/dummy/app/views/application/welcome.xml.builder +2 -0
  22. data/spec/dummy/app/views/blog/links/index.html +4 -0
  23. data/spec/dummy/app/views/blog/links/show.html +4 -0
  24. data/spec/dummy/app/views/blog/posts/index.html +4 -0
  25. data/spec/dummy/app/views/blog/posts/show.html +4 -0
  26. data/spec/dummy/app/views/layouts/_navigation.html.erb +73 -0
  27. data/spec/dummy/app/views/layouts/application.html.erb +40 -0
  28. data/spec/dummy/config.ru +4 -0
  29. data/spec/dummy/config/application.rb +57 -0
  30. data/spec/dummy/config/boot.rb +9 -0
  31. data/spec/dummy/config/environment.rb +5 -0
  32. data/spec/dummy/config/environments/development.rb +37 -0
  33. data/spec/dummy/config/environments/test.rb +37 -0
  34. data/spec/dummy/config/evergreen.rb +47 -0
  35. data/spec/dummy/config/initializers/additional_navigasmic.rb +14 -0
  36. data/spec/dummy/config/initializers/navigasmic.rb +145 -0
  37. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  38. data/spec/dummy/config/initializers/session_store.rb +8 -0
  39. data/spec/dummy/config/locales/en.yml +5 -0
  40. data/spec/dummy/config/routes.rb +68 -0
  41. data/spec/dummy/log/.gitkeep +0 -0
  42. data/spec/dummy/public/bootstrap.min.css +9 -0
  43. data/spec/dummy/public/bootstrap.min.js +6 -0
  44. data/spec/dummy/public/favicon.ico +0 -0
  45. data/spec/dummy/public/jquery.min.js +2 -0
  46. data/spec/dummy/script/rails +6 -0
  47. data/spec/spec_helper.rb +39 -0
  48. metadata +139 -63
  49. data/.document +0 -5
  50. data/README.textile +0 -276
  51. data/Rakefile +0 -57
  52. data/VERSION +0 -1
  53. data/lib/builders/html_builder.rb +0 -69
  54. data/lib/builders/xml_builder.rb +0 -51
  55. data/navigasmic.gemspec +0 -49
  56. data/test/navigasmic_test.rb +0 -7
  57. 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,2 @@
1
+ require 'navigasmic/rails/view_helpers'
2
+ require 'navigasmic/rails/engine'
@@ -0,0 +1,9 @@
1
+ require 'rails'
2
+
3
+ module Navigasmic
4
+ class Engine < ::Rails::Engine
5
+ initializer "navigasmic.view_helpers" do
6
+ ActionView::Base.send :include, ViewHelpers
7
+ end
8
+ end
9
+ 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
@@ -0,0 +1,3 @@
1
+ module Navigasmic
2
+ VERSION = '1.0.0'
3
+ end
@@ -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,13 @@
1
+ class ApplicationController < ActionController::Base
2
+
3
+ helper_method :logged_in?
4
+
5
+ def welcome; end
6
+
7
+ private
8
+
9
+ def logged_in?
10
+ true
11
+ end
12
+
13
+ end
@@ -0,0 +1,7 @@
1
+ class Blog::LinksController < ApplicationController
2
+
3
+ layout 'application'
4
+
5
+ def index; end
6
+ def show; end
7
+ end
@@ -0,0 +1,7 @@
1
+ class Blog::PostsController < ApplicationController
2
+
3
+ layout 'application'
4
+
5
+ def index; end
6
+ def show; end
7
+ end
@@ -0,0 +1,4 @@
1
+ ApplicationController#welcome
2
+ <br/>
3
+ <br/>
4
+ <br/>
@@ -0,0 +1,2 @@
1
+ xml.instruct!
2
+ xml << semantic_navigation(:testing, builder: Navigasmic::Builder::MapBuilder)
@@ -0,0 +1,4 @@
1
+ Blog::LinksController#index
2
+ <br/>
3
+ <br/>
4
+ <a href="/blog/links/1">#show</a>
@@ -0,0 +1,4 @@
1
+ Blog::LinksController#show
2
+ <br/>
3
+ <br/>
4
+ <a href="/blog/links">#index</a>
@@ -0,0 +1,4 @@
1
+ Blog::PostsController#index
2
+ <br/>
3
+ <br/>
4
+ <a href="/blog/posts/1">#show</a>
@@ -0,0 +1,4 @@
1
+ Blog::PostsController#show
2
+ <br/>
3
+ <br/>
4
+ <a href="/blog/posts">#index</a>
@@ -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 %>