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.
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 %>