simple-navigation-ext 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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