simple-navigation-ext 0.0.1

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 (46) hide show
  1. data/CHANGELOG +177 -0
  2. data/README +22 -0
  3. data/Rakefile +48 -0
  4. data/VERSION +1 -0
  5. data/generators/navigation_config/USAGE +1 -0
  6. data/generators/navigation_config/navigation_config_generator.rb +8 -0
  7. data/generators/navigation_config/templates/config/navigation.rb +67 -0
  8. data/lib/generators/navigation_config/navigation_config_generator.rb +12 -0
  9. data/lib/simple-navigation.rb +1 -0
  10. data/lib/simple_navigation/adapters/base.rb +37 -0
  11. data/lib/simple_navigation/adapters/padrino.rb +20 -0
  12. data/lib/simple_navigation/adapters/rails.rb +94 -0
  13. data/lib/simple_navigation/adapters/sinatra.rb +68 -0
  14. data/lib/simple_navigation/adapters.rb +9 -0
  15. data/lib/simple_navigation/core/configuration.rb +70 -0
  16. data/lib/simple_navigation/core/item.rb +117 -0
  17. data/lib/simple_navigation/core/item_adapter.rb +63 -0
  18. data/lib/simple_navigation/core/item_container.rb +146 -0
  19. data/lib/simple_navigation/core/items_provider.rb +35 -0
  20. data/lib/simple_navigation/core.rb +5 -0
  21. data/lib/simple_navigation/rails_controller_methods.rb +144 -0
  22. data/lib/simple_navigation/rendering/helpers.rb +82 -0
  23. data/lib/simple_navigation/rendering/renderer/base.rb +71 -0
  24. data/lib/simple_navigation/rendering/renderer/breadcrumbs.rb +37 -0
  25. data/lib/simple_navigation/rendering/renderer/links.rb +25 -0
  26. data/lib/simple_navigation/rendering/renderer/list.rb +47 -0
  27. data/lib/simple_navigation/rendering.rb +10 -0
  28. data/lib/simple_navigation.rb +162 -0
  29. data/rails/init.rb +1 -0
  30. data/spec/lib/simple_navigation/adapters/padrino_spec.rb +29 -0
  31. data/spec/lib/simple_navigation/adapters/rails_spec.rb +284 -0
  32. data/spec/lib/simple_navigation/adapters/sinatra_spec.rb +60 -0
  33. data/spec/lib/simple_navigation/core/configuration_spec.rb +122 -0
  34. data/spec/lib/simple_navigation/core/item_adapter_spec.rb +209 -0
  35. data/spec/lib/simple_navigation/core/item_container_spec.rb +396 -0
  36. data/spec/lib/simple_navigation/core/item_spec.rb +513 -0
  37. data/spec/lib/simple_navigation/core/items_provider_spec.rb +60 -0
  38. data/spec/lib/simple_navigation/rails_controller_methods_spec.rb +249 -0
  39. data/spec/lib/simple_navigation/rendering/helpers_spec.rb +183 -0
  40. data/spec/lib/simple_navigation/rendering/renderer/base_spec.rb +199 -0
  41. data/spec/lib/simple_navigation/rendering/renderer/breadcrumbs_spec.rb +58 -0
  42. data/spec/lib/simple_navigation/rendering/renderer/links_spec.rb +58 -0
  43. data/spec/lib/simple_navigation/rendering/renderer/list_spec.rb +189 -0
  44. data/spec/lib/simple_navigation_spec.rb +307 -0
  45. data/spec/spec_helper.rb +96 -0
  46. metadata +158 -0
@@ -0,0 +1,117 @@
1
+ require 'ruby-debug'
2
+
3
+ module SimpleNavigation
4
+
5
+ # Represents an item in your navigation. Gets generated by the item method in the config-file.
6
+ class Item
7
+ attr_reader :key, :name, :url, :sub_navigation, :method, :highlights_on, :exclude_highlighting
8
+ attr_writer :html_options
9
+
10
+ # see ItemContainer#item
11
+ #
12
+ # The subnavigation (if any) is either provided by a block or passed in directly as <tt>items</tt>
13
+ def initialize(container, key, name, url, options, items=nil, &sub_nav_block)
14
+ @container = container
15
+ @container.dom_class = options.delete(:container_class) if options[:container_class]
16
+ @container.dom_id = options.delete(:container_id) if options[:container_id]
17
+ @key = key
18
+ @method = options.delete(:method)
19
+ @name = name
20
+ @url = url.instance_of?(Proc) ? url.call : url
21
+ @highlights_on = options.delete(:highlights_on)
22
+ @exclude_highlighting = options.delete(:exclude_highlight_if)
23
+ @html_options = options
24
+ if sub_nav_block || items
25
+ @sub_navigation = ItemContainer.new(@container.level + 1)
26
+ sub_nav_block.call @sub_navigation if sub_nav_block
27
+ @sub_navigation.items = items if items
28
+ end
29
+ end
30
+
31
+ # Returns true if this navigation item should be rendered as 'selected'.
32
+ # An item is selected if
33
+ #
34
+ # * it has been explicitly selected in a controller or
35
+ # * it has a subnavigation and one of its subnavigation items is selected or
36
+ # * its url matches the url of the current request (auto highlighting)
37
+ #
38
+ def selected?
39
+ @selected = (@selected || selected_by_config? || selected_by_subnav? || selected_by_url?)
40
+ end
41
+
42
+ # Returns the html-options hash for the item, i.e. the options specified for this item in the config-file.
43
+ # It also adds the 'selected' class to the list of classes if necessary.
44
+ def html_options
45
+ default_options = self.autogenerate_item_ids? ? {:id => autogenerated_item_id} : {}
46
+ options = default_options.merge(@html_options)
47
+ options[:class] = [@html_options[:class], self.selected_class].flatten.compact.join(' ')
48
+ options.delete(:class) if options[:class].nil? || options[:class] == ''
49
+ options
50
+ end
51
+
52
+ def item_excluded
53
+ if exclude_highlighting
54
+ raise ArgumentError, ':exclude_highlight_if must be a regexp' unless exclude_highlighting.instance_of?(Regexp)
55
+ SimpleNavigation.request_uri =~ exclude_highlighting
56
+ end
57
+ end
58
+
59
+ # Returns the configured selected_class if the item is selected, nil otherwise
60
+ def selected_class
61
+ if selected?
62
+ self.item_excluded ? nil : SimpleNavigation.config.selected_class
63
+ # SimpleNavigation.config.selected_class
64
+ else
65
+ nil
66
+ end
67
+ end
68
+
69
+ protected
70
+
71
+ # Returns true if item has a subnavigation and the sub_navigation is selected
72
+ def selected_by_subnav?
73
+ sub_navigation && sub_navigation.selected?
74
+ end
75
+
76
+ def selected_by_config?
77
+ false
78
+ end
79
+
80
+ # Returns true if the item's url matches the request's current url.
81
+ def selected_by_url?
82
+ if highlights_on
83
+ raise ArgumentError, ':highlights_on must be a regexp' unless highlights_on.instance_of?(Regexp)
84
+ SimpleNavigation.request_uri =~ highlights_on
85
+ elsif auto_highlight?
86
+ !!(root_path_match? || SimpleNavigation.current_page?(url_without_anchor))
87
+ else
88
+ false
89
+ end
90
+ end
91
+
92
+ # Returns true if both the item's url and the request's url are root_path
93
+ def root_path_match?
94
+ url == '/' && SimpleNavigation.request_path == '/'
95
+ end
96
+
97
+ # Returns true if the item's id should be added to the rendered output.
98
+ def autogenerate_item_ids?
99
+ SimpleNavigation.config.autogenerate_item_ids
100
+ end
101
+
102
+ # Returns the item's id which is added to the rendered output.
103
+ def autogenerated_item_id
104
+ SimpleNavigation.config.id_generator.call(key)
105
+ end
106
+
107
+ # Return true if auto_highlight is on for this item.
108
+ def auto_highlight?
109
+ SimpleNavigation.config.auto_highlight && @container.auto_highlight
110
+ end
111
+
112
+ def url_without_anchor
113
+ url.split('#').first
114
+ end
115
+
116
+ end
117
+ end
@@ -0,0 +1,63 @@
1
+ require 'forwardable'
2
+
3
+ module SimpleNavigation
4
+
5
+ # This class acts as an adapter to items that are not defined using the DSL in the config/navigation.rb, but directly provided inside the application.
6
+ # When defining the items that way, every item you provide needs to define the following methods:
7
+ #
8
+ # * <tt>key</tt>
9
+ # * <tt>name</tt>
10
+ # * <tt>url</tt>
11
+ #
12
+ # and optionally
13
+ #
14
+ # * <tt>options</tt>
15
+ # * <tt>items</tt> - if one of your items has a subnavigation it must respond to <tt>items</tt> providing the subnavigation.
16
+ #
17
+ # You can also specify your items as a list of hashes. The hashes will be converted to objects automatically.
18
+ # The hashes representing the items obviously must have the keys :key, :name and :url and optionally the keys :options and :items.
19
+ #
20
+ # See SimpleNavigation::ItemContainer#item for the purpose of these methods.
21
+ class ItemAdapter
22
+ extend Forwardable
23
+
24
+ def_delegators :item, :key, :name, :url
25
+
26
+ attr_reader :item
27
+
28
+ def initialize(item)
29
+ @item = item.instance_of?(Hash) ? to_object(item) : item
30
+ end
31
+
32
+ # Returns the options for this item. If the wrapped item does not implement an options method, an empty hash is returned.
33
+ def options
34
+ @item.respond_to?(:options) ? @item.options : {}
35
+ end
36
+
37
+ # Returns the items (subnavigation) for this item if it responds to :items and the items-collection is not empty. Returns nil otherwise.
38
+ def items
39
+ (@item.respond_to?(:items) && !(@item.items.nil? || @item.items.empty?)) ? @item.items : nil
40
+ end
41
+
42
+ # Converts this Item into a SimpleNavigation::Item
43
+ def to_simple_navigation_item(item_container)
44
+ SimpleNavigation::Item.new(item_container, key, name, url, options, items)
45
+ end
46
+
47
+ protected
48
+
49
+ # Converts the specified hash into an object. Each key will be added as method.
50
+ #
51
+ def to_object(hash)
52
+ mod = Module.new do
53
+ hash.each_pair do |key, value|
54
+ define_method key do
55
+ value
56
+ end
57
+ end
58
+ end
59
+ Object.new.extend(mod)
60
+ end
61
+
62
+ end
63
+ end
@@ -0,0 +1,146 @@
1
+ module SimpleNavigation
2
+
3
+ # Holds the Items for a navigation 'level'.
4
+ class ItemContainer
5
+
6
+ attr_reader :items, :level
7
+ attr_accessor :renderer, :dom_id, :dom_class, :auto_highlight
8
+
9
+ def initialize(level=1) #:nodoc:
10
+ @level = level
11
+ @items = []
12
+ @renderer = SimpleNavigation.config.renderer
13
+ @auto_highlight = true
14
+ end
15
+
16
+ # Creates a new navigation item.
17
+ #
18
+ # The <tt>key</tt> is a symbol which uniquely defines your navigation item in the scope of the primary_navigation or the sub_navigation.
19
+ #
20
+ # The <tt>name</tt> will be displayed in the rendered navigation. This can also be a call to your I18n-framework.
21
+ #
22
+ # The <tt>url</tt> is the address that the generated item points to. You can also use url_helpers (named routes, restful routes helper, url_for etc.)
23
+ #
24
+ # The <tt>options</tt> can be used to specify the following things:
25
+ # * <tt>any html_attributes</tt> - will be included in the rendered navigation item (e.g. id, class etc.)
26
+ # * <tt>:if</tt> - Specifies a proc to call to determine if the item should
27
+ # be rendered (e.g. <tt>:if => Proc.new { current_user.admin? }</tt>). The
28
+ # proc should evaluate to a true or false value and is evaluated in the context of the view.
29
+ # * <tt>:unless</tt> - Specifies a proc to call to determine if the item should not
30
+ # be rendered (e.g. <tt>:unless => Proc.new { current_user.admin? }</tt>). The
31
+ # proc should evaluate to a true or false value and is evaluated in the context of the view.
32
+ # * <tt>:method</tt> - Specifies the http-method for the generated link - default is :get.
33
+ # * <tt>:highlights_on</tt> - if autohighlighting is turned off and/or you want to explicitly specify
34
+ # when the item should be highlighted, you can set a regexp which is matched againstthe current URI.
35
+ #
36
+ # The <tt>block</tt> - if specified - will hold the item's sub_navigation.
37
+ def item(key, name, url, options={}, &block)
38
+ (@items << SimpleNavigation::Item.new(self, key, name, url, options, nil, &block)) if should_add_item?(options)
39
+ end
40
+
41
+ def items=(items)
42
+ items.each do |item|
43
+ item = SimpleNavigation::ItemAdapter.new(item)
44
+ (@items << item.to_simple_navigation_item(self)) if should_add_item?(item.options)
45
+ end
46
+ end
47
+
48
+ # Returns the Item with the specified key, nil otherwise.
49
+ #
50
+ def [](navi_key)
51
+ items.find {|i| i.key == navi_key}
52
+ end
53
+
54
+ # Returns the level of the item specified by navi_key.
55
+ # Recursively works its way down the item's sub_navigations if the desired item is not found directly in this container's items.
56
+ # Returns nil item cannot be found.
57
+ #
58
+ def level_for_item(navi_key)
59
+ my_item = self[navi_key]
60
+ return self.level if my_item
61
+ items.each do |i|
62
+ if i.sub_navigation
63
+ level = i.sub_navigation.level_for_item(navi_key)
64
+ return level unless level.nil?
65
+ end
66
+ end
67
+ return nil
68
+ end
69
+
70
+ # Renders the items in this ItemContainer using the configured renderer.
71
+ #
72
+ # The options are the same as in the view's render_navigation call (they get passed on)
73
+ def render(options={})
74
+ renderer_instance = if options[:renderer]
75
+ if options[:renderer].instance_of?(Symbol) && SimpleNavigation.registered_renderers.key?(options[:renderer])
76
+ SimpleNavigation.registered_renderers[options[:renderer]].new(options)
77
+ else
78
+ options[:renderer].new(options)
79
+ end
80
+ else
81
+ self.renderer.new(options)
82
+ end
83
+ renderer_instance.render(self)
84
+ end
85
+
86
+ # Returns true if any of this container's items is selected.
87
+ #
88
+ def selected?
89
+ items.any? {|i| i.selected?}
90
+ end
91
+
92
+ # Returns the currently selected item, nil if no item is selected.
93
+ #
94
+ def selected_item
95
+ items.find {|i| i.selected?}
96
+ end
97
+
98
+ # Returns the active item_container for the specified level
99
+ # (recursively looks up items in selected sub_navigation if level is deeper than this container's level).
100
+ #
101
+ def active_item_container_for(desired_level)
102
+ return self if self.level == desired_level
103
+ return nil unless selected_sub_navigation?
104
+ return selected_item.sub_navigation.active_item_container_for(desired_level)
105
+ end
106
+
107
+ # Returns the deepest possible active item_container.
108
+ # (recursively searches in the sub_navigation if this container has a selected sub_navigation).
109
+ def active_leaf_container
110
+ if selected_sub_navigation?
111
+ selected_item.sub_navigation.active_leaf_container
112
+ else
113
+ self
114
+ end
115
+ end
116
+
117
+ # Returns true if there are no items defined for this container.
118
+ def empty?
119
+ items.empty?
120
+ end
121
+
122
+ private
123
+
124
+ def selected_sub_navigation?
125
+ !!(selected_item && selected_item.sub_navigation)
126
+ end
127
+
128
+ # partially borrowed from ActionSupport::Callbacks
129
+ def should_add_item?(options) #:nodoc:
130
+ [options.delete(:if)].flatten.compact.all? { |m| evaluate_method(m) } &&
131
+ ![options.delete(:unless)].flatten.compact.any? { |m| evaluate_method(m) }
132
+ end
133
+
134
+ # partially borrowed from ActionSupport::Callbacks
135
+ def evaluate_method(method) #:nodoc:
136
+ case method
137
+ when Proc, Method
138
+ method.call
139
+ else
140
+ raise ArgumentError, ":if or :unless must be procs or lambdas"
141
+ end
142
+ end
143
+
144
+ end
145
+
146
+ end
@@ -0,0 +1,35 @@
1
+ module SimpleNavigation
2
+
3
+ # Acts as a proxy to navigation items that are passed into the SimpleNavigation::Configuration#items method. It hides the logic
4
+ # for finding items from the Configuration object.
5
+ #
6
+ class ItemsProvider
7
+
8
+ attr_reader :provider
9
+
10
+ # It accepts the following types of provider:
11
+ # * methodname as symbol - the specified method should return the relevant items and has to be available in the view (a helper method)
12
+ # * object that responds to :items
13
+ # * enumerable object that represents the items
14
+ #
15
+ # See SimpleNavigation::ItemAdapter for the requirements that need to be fulfilled by the provided items.
16
+ #
17
+ def initialize(provider)
18
+ @provider = provider
19
+ end
20
+
21
+ # Returns the navigation items
22
+ def items
23
+ if provider.instance_of?(Symbol)
24
+ SimpleNavigation.context_for_eval.send(provider)
25
+ elsif provider.respond_to?(:items)
26
+ provider.items
27
+ elsif provider.respond_to?(:each)
28
+ provider
29
+ else
30
+ raise "items_provider either must be a symbol specifying the helper-method to call, an object with an items-method defined or an enumerable representing the items"
31
+ end
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,5 @@
1
+ require 'simple_navigation/core/configuration'
2
+ require 'simple_navigation/core/item_adapter'
3
+ require 'simple_navigation/core/item'
4
+ require 'simple_navigation/core/item_container'
5
+ require 'simple_navigation/core/items_provider'
@@ -0,0 +1,144 @@
1
+ module SimpleNavigation
2
+
3
+ class << self
4
+
5
+ def explicit_navigation_args
6
+ self.adapter.controller.instance_variable_get(:"@sn_current_navigation_args")
7
+ end
8
+
9
+ # Reads the current navigation for the specified level from the controller.
10
+ # Returns nil if there is no current navigation set for level.
11
+ def current_navigation_for(level)
12
+ self.adapter.controller.instance_variable_get(:"@sn_current_navigation_#{level}")
13
+ end
14
+
15
+ # If any navigation has been explicitely set in the controller this method evaluates the specified args set in the controller and sets
16
+ # the correct instance variable in the controller.
17
+ def handle_explicit_navigation
18
+ if SimpleNavigation.explicit_navigation_args
19
+ level, navigation = parse_explicit_navigation_args
20
+ self.adapter.controller.instance_variable_set(:"@sn_current_navigation_#{level}", navigation)
21
+ end
22
+ end
23
+
24
+ # TODO: refactor this ugly thing to make it nice and short
25
+ def parse_explicit_navigation_args
26
+ args = SimpleNavigation.explicit_navigation_args
27
+ args = [Hash.new] if args.empty?
28
+ if args.first.kind_of? Hash
29
+ options = args.first
30
+ else # args is a list of current navigation for several levels
31
+ options = {}
32
+ if args.size == 1 #only one navi-key has been specified, try to find out level
33
+ level = SimpleNavigation.primary_navigation.level_for_item(args.first)
34
+ options[:"level_#{level}"] = args.first if level
35
+ else
36
+ args.each_with_index {|arg, i| options[:"level_#{i + 1}"] = arg}
37
+ end
38
+ end
39
+ #only the deepest level is relevant
40
+ level = options.inject(0) do |max, kv|
41
+ kv.first.to_s =~ /level_(\d)/
42
+ max = $1.to_i if $1.to_i > max
43
+ max
44
+ end
45
+ raise ArgumentError, "Invalid level specified or item key not found" if level == 0
46
+ [level, options[:"level_#{level}"]]
47
+ end
48
+
49
+ end
50
+
51
+ # Adds methods for explicitely setting the current 'active' navigation to the controllers.
52
+ # Since version 2.0.0 the simple_navigation plugin determines the active navigation based on the current url by default (auto highlighting),
53
+ # so explicitely defining the active navigation in the controllers is only needed for edge cases where automatic highlighting does not work.
54
+ #
55
+ # On the controller class level, use the <tt>navigation</tt> method to set the active navigation for all actions in the controller.
56
+ # Let's assume that we have a primary navigation item :account which in turn has a sub navigation item :settings.
57
+ #
58
+ # ==== Examples
59
+ # class AccountController << ActionController
60
+ # navigation :account
61
+ # ...
62
+ # end
63
+ #
64
+ # class AccountSettingsController << ActionController
65
+ # navigation :settings
66
+ # ...
67
+ # end
68
+ #
69
+ # The first example sets the current primary navigation to :account for all actions. No active sub_navigation.
70
+ # The second example sets the current sub navigation to :settings and since it is a child of :account the current primary navigation is set to :account.
71
+ #
72
+ # On the controller instance level, use the <tt>current_navigation</tt> method to define the active navigation for a specific action.
73
+ # The navigation item that is set in <tt>current_navigation</tt> overrides the one defined on the controller class level (see <tt>navigation</tt> method).
74
+ # Thus if you have an :account primary item with a :special sub navigation item:
75
+ #
76
+ # ==== Example
77
+ # class AccountController << ActionController
78
+ # navigation :account
79
+ #
80
+ # def your_special_action
81
+ # ...
82
+ # current_navigation :special
83
+ # end
84
+ # end
85
+ #
86
+ # The code above still sets the active primary navigation to :account for all actions, but sets the sub_navigation to :account -> :special for 'your_special_action'.
87
+ #
88
+ # Note 1: As you can see above you just have to set the navigation item of your 'deepest' navigation level as active and all its parents are marked as active, too.
89
+ #
90
+ # Note 2: The specified symbols must match the keys for your navigation items in your config/navigation.rb file.
91
+ module ControllerMethods
92
+ def self.included(base) #:nodoc:
93
+ base.class_eval do
94
+ extend ClassMethods
95
+ include InstanceMethods
96
+ end
97
+ end
98
+
99
+ module ClassMethods
100
+ # Sets the active navigation for all actions in this controller.
101
+ #
102
+ # The specified symbol must match the keys for your navigation items in your config/navigation.rb file.
103
+ def navigation(*args)
104
+ self.class_eval do
105
+ define_method :sn_set_navigation do
106
+ current_navigation(*args)
107
+ end
108
+ protected :sn_set_navigation
109
+ before_filter :sn_set_navigation
110
+ end
111
+ end
112
+ end
113
+
114
+ module InstanceMethods
115
+ # Sets the active navigation. Call this method in any action to override the controller-wide active navigation
116
+ # specified by navigation.
117
+ #
118
+ # The specified symbol must match the keys for your navigation items in your config/navigation.rb file.
119
+ def current_navigation(*args)
120
+ @sn_current_navigation_args = args
121
+ end
122
+ end
123
+
124
+ end
125
+
126
+ class Item
127
+
128
+ def selected_by_config?
129
+ key == SimpleNavigation.current_navigation_for(@container.level)
130
+ end
131
+
132
+ end
133
+
134
+ class ItemContainer
135
+
136
+ def selected_item
137
+ self[SimpleNavigation.current_navigation_for(self.level)] || items.find {|i| i.selected?}
138
+ end
139
+
140
+ end
141
+
142
+ end
143
+
144
+ ActionController::Base.send(:include, SimpleNavigation::ControllerMethods)
@@ -0,0 +1,82 @@
1
+ module SimpleNavigation
2
+
3
+ # View helpers to render the navigation.
4
+ #
5
+ # Use render_navigation as following to render your navigation:
6
+ # * call <tt>render_navigation</tt> without :level option to render your complete navigation as nested tree.
7
+ # * call <tt>render_navigation(:level => x)</tt> to render a specific navigation level (e.g. :level => 1 to render your primary navigation, :level => 2 to render the sub navigation and so forth)
8
+ # * call <tt>render_navigation(:level => 2..3)</tt> to render navigation levels 2 and 3).
9
+ # For example, you could use render_navigation(:level => 1) to render your primary navigation as tabs and render_navigation(:level => 2..3) to render the rest of the navigation as a tree in a sidebar.
10
+ #
11
+ # ==== Examples (using Haml)
12
+ # #primary_navigation= render_navigation(:level => 1)
13
+ #
14
+ # #sub_navigation= render_navigation(:level => 2)
15
+ #
16
+ # #nested_navigation= render_navigation
17
+ #
18
+ # #top_navigation= render_navigation(:level => 1..2)
19
+ # #sidebar_navigation= render_navigation(:level => 3)
20
+ module Helpers
21
+
22
+ # Renders the navigation according to the specified options-hash.
23
+ #
24
+ # The following options are supported:
25
+ # * <tt>:level</tt> - defaults to :all which renders the the sub_navigation for an active primary_navigation inside that active primary_navigation item.
26
+ # Specify a specific level to only render that level of navigation (e.g. :level => 1 for primary_navigation etc...).
27
+ # Specifiy a Range of levels to render only those specific levels (e.g. :level => 1..2 to render both your first and second levels, maybe you want to render your third level somewhere else on the page)
28
+ # * <tt>:expand_all</tt> - defaults to false. If set to true the all specified levels will be rendered as a fully expanded tree (always open). This is useful for javascript menus like Superfish.
29
+ # * <tt>:context</tt> - specifies the context for which you would render the navigation. Defaults to :default which loads the default navigation.rb (i.e. config/navigation.rb).
30
+ # If you specify a context then the plugin tries to load the configuration file for that context, e.g. if you call <tt>render_navigation(:context => :admin)</tt> the file config/admin_navigation.rb
31
+ # will be loaded and used for rendering the navigation.
32
+ # * <tt>:items</tt> - you can specify the items directly (e.g. if items are dynamically generated from database). See SimpleNavigation::ItemsProvider for documentation on what to provide as items.
33
+ # * <tt>:renderer</tt> - specify the renderer to be used for rendering the navigation. Either provide the Class or a symbol matching a registered renderer. Defaults to :list (html list renderer).
34
+ def render_navigation(options={})
35
+ options = apply_defaults(options)
36
+ load_config(options)
37
+ active_item_container = SimpleNavigation.active_item_container_for(options[:level])
38
+ active_item_container.render(options) unless active_item_container.nil?
39
+ end
40
+
41
+ # Returns the name of the currently active navigation item belonging to the specified level.
42
+ #
43
+ # The following options are supported:
44
+ # * <tt>:level</tt> - defaults to :all which returns the most specific/deepest selected item (the leaf).
45
+ # Specify a specific level to only look for the selected item in the specified level of navigation (e.g. :level => 1 for primary_navigation etc...).
46
+ # * <tt>:context</tt> - specifies the context for which you would like to find the active navigation item. Defaults to :default which loads the default navigation.rb (i.e. config/navigation.rb).
47
+ # If you specify a context then the plugin tries to load the configuration file for that context, e.g. if you call <tt>active_navigation_item_name(:context => :admin)</tt> the file config/admin_navigation.rb
48
+ # will be loaded and used for searching the active item.
49
+ # * <tt>:items</tt> - you can specify the items directly (e.g. if items are dynamically generated from database). See SimpleNavigation::ItemsProvider for documentation on what to provide as items.
50
+ #
51
+ # Returns an empty string if no active item can be found for the specified options
52
+ def active_navigation_item_name(options={})
53
+ options = apply_defaults(options)
54
+ load_config(options)
55
+ options[:level] = :leaves if options[:level] == :all
56
+ active_item_container = SimpleNavigation.active_item_container_for(options[:level])
57
+ if active_item_container && !active_item_container.selected_item.nil?
58
+ active_item_container.selected_item.name
59
+ else
60
+ ''
61
+ end
62
+ end
63
+
64
+ private
65
+
66
+ def load_config(options)
67
+ ctx = options.delete(:context)
68
+ SimpleNavigation.init_adapter_from self
69
+ SimpleNavigation.load_config(ctx)
70
+ SimpleNavigation::Configuration.eval_config(ctx)
71
+ SimpleNavigation.config.items(options[:items]) if options[:items]
72
+ SimpleNavigation.handle_explicit_navigation if SimpleNavigation.respond_to?(:handle_explicit_navigation)
73
+ raise "no primary navigation defined, either use a navigation config file or pass items directly to render_navigation" unless SimpleNavigation.primary_navigation
74
+ end
75
+
76
+ def apply_defaults(options)
77
+ options[:level] = options.delete(:levels) if options[:levels]
78
+ {:context => :default, :level => :all}.merge(options)
79
+ end
80
+
81
+ end
82
+ end
@@ -0,0 +1,71 @@
1
+ require 'forwardable'
2
+
3
+ module SimpleNavigation
4
+ module Renderer
5
+
6
+ # This is the base class for all renderers.
7
+ #
8
+ # A renderer is responsible for rendering an ItemContainer and its containing items to HTML.
9
+ class Base
10
+ extend Forwardable
11
+
12
+ attr_reader :options, :adapter
13
+
14
+ def_delegators :adapter, :link_to, :content_tag
15
+
16
+ def initialize(options) #:nodoc:
17
+ @options = options
18
+ @adapter = SimpleNavigation.adapter
19
+ end
20
+
21
+ def expand_all?
22
+ !!options[:expand_all]
23
+ end
24
+
25
+ def level
26
+ options[:level] || :all
27
+ end
28
+
29
+ def skip_if_empty?
30
+ !!options[:skip_if_empty]
31
+ end
32
+
33
+ def include_sub_navigation?(item)
34
+ consider_sub_navigation?(item) && expand_sub_navigation?(item)
35
+ end
36
+
37
+ def render_sub_navigation_for(item)
38
+ item.sub_navigation.render(self.options)
39
+ end
40
+
41
+ # Renders the specified ItemContainer to HTML.
42
+ #
43
+ # When implementing a renderer, please consider to call include_sub_navigation? to determin
44
+ # whether an item's sub_navigation should be rendered or not.
45
+ #
46
+ def render(item_container)
47
+ raise 'subclass responsibility'
48
+ end
49
+
50
+ protected
51
+
52
+ def consider_sub_navigation?(item)
53
+ return false if item.sub_navigation.nil?
54
+ case level
55
+ when :all
56
+ return true
57
+ when Integer
58
+ return false
59
+ when Range
60
+ return item.sub_navigation.level <= level.max
61
+ end
62
+ false
63
+ end
64
+
65
+ def expand_sub_navigation?(item)
66
+ expand_all? || item.selected?
67
+ end
68
+
69
+ end
70
+ end
71
+ end