strikeroff-simple-navigation 2.0.2

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.
@@ -0,0 +1,31 @@
1
+ module SimpleNavigation
2
+ module Renderer
3
+
4
+ # Renders an ItemContainer as a <ul> element and its containing items as <li> elements.
5
+ # It adds the 'selected' class to li element AND the link inside the li element that is currently active.
6
+ #
7
+ # If the sub navigation should be included, it renders another <ul> containing the sub navigation inside the active <li> element.
8
+ #
9
+ # If the SimpleNavigation.config.render_all_levels option is set to true, it always renders all levels of navigation (fully expanded tree).
10
+ #
11
+ # By default, the renderer sets the item's key as dom_id for the rendered <li> element unless the config option <tt>autogenerate_item_ids</tt> is set to false.
12
+ # The id can also be explicitely specified by setting the id in the html-options of the 'item' method in the config/navigation.rb file.
13
+ class List < SimpleNavigation::Renderer::Base
14
+
15
+ def render(item_container, include_sub_navigation=false, options={})
16
+ list_content = item_container.items.inject([]) do |list, item|
17
+ html_options = item.html_options
18
+ li_content = link_to(item.name, item.url, :class => item.selected_class, :method => item.method)
19
+ if item.sub_navigation
20
+ if SimpleNavigation.config.render_all_levels || (include_sub_navigation && item.selected?) || options[:all]
21
+ li_content << (item.sub_navigation.render(include_sub_navigation))
22
+ end
23
+ end
24
+ list << content_tag(:li, li_content, html_options)
25
+ end
26
+ content_tag(:ul, list_content.join, {:id => item_container.dom_id, :class => item_container.dom_class})
27
+ end
28
+
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,118 @@
1
+ # Load all source files (if this is not done explicitly some naming conflicts may occur if rails app has classes with the same name)
2
+ require 'simple_navigation/configuration'
3
+ require 'simple_navigation/helpers'
4
+ require 'simple_navigation/controller_methods'
5
+ require 'simple_navigation/item'
6
+ require 'simple_navigation/item_container'
7
+ require 'simple_navigation/renderer/base'
8
+ require 'simple_navigation/renderer/list'
9
+
10
+ # A plugin for generating a simple navigation. See README for resources on usage instructions.
11
+ module SimpleNavigation
12
+
13
+ mattr_accessor :config_files, :config_file_path, :controller, :template, :explicit_current_navigation
14
+
15
+ self.config_files = {}
16
+
17
+ class << self
18
+
19
+ # Reads the config_file for the specified navigation_context and stores it for later evaluation.
20
+ def load_config(navigation_context = :default)
21
+ raise "config_file_path is not set!" unless self.config_file_path
22
+ raise "Config file '#{config_file_name(navigation_context)}' does not exists!" unless File.exists?(config_file_name(navigation_context))
23
+ if ::RAILS_ENV == 'production'
24
+ self.config_files[navigation_context] ||= IO.read(config_file_name(navigation_context))
25
+ else
26
+ self.config_files[navigation_context] = IO.read(config_file_name(navigation_context))
27
+ end
28
+ end
29
+
30
+ # Returns the singleton instance of the SimpleNavigation::Configuration
31
+ def config
32
+ SimpleNavigation::Configuration.instance
33
+ end
34
+
35
+ # Returns the ItemContainer that contains the items for the primary navigation
36
+ def primary_navigation
37
+ config.primary_navigation
38
+ end
39
+
40
+ # Returns the path to the config_file for the given navigation_context
41
+ def config_file_name(navigation_context = :default)
42
+ file_name = navigation_context == :default ? '' : "#{navigation_context.to_s.underscore}_"
43
+ File.join(config_file_path, "#{file_name}navigation.rb")
44
+ end
45
+
46
+ def explicit_navigation_args
47
+ self.controller.instance_variable_get(:"@sn_current_navigation_args")
48
+ end
49
+
50
+ # Reads the current navigation for the specified level from the controller.
51
+ # Returns nil if there is no current navigation set for level.
52
+ def current_navigation_for(level)
53
+ self.controller.instance_variable_get(:"@sn_current_navigation_#{level}")
54
+ end
55
+
56
+ def active_item_container_for(level)
57
+ self.primary_navigation.active_item_container_for(level)
58
+ end
59
+
60
+ # If any navigation has been explicitely set in the controller this method evaluates the specified args set in the controller and sets
61
+ # the correct instance variable in the controller.
62
+ def handle_explicit_navigation
63
+ if SimpleNavigation.explicit_navigation_args
64
+ begin
65
+ level, navigation = parse_explicit_navigation_args
66
+ self.controller.instance_variable_set(:"@sn_current_navigation_#{level}", navigation)
67
+ rescue
68
+ #we do nothing here
69
+ #TODO: check if this is the right way to handle wrong explicit navigation
70
+ end
71
+ end
72
+ end
73
+
74
+ private
75
+
76
+ # TODO: refactor this ugly thing to make it nice and short
77
+ def parse_explicit_navigation_args
78
+ args = SimpleNavigation.explicit_navigation_args
79
+ args = [Hash.new] if args.empty?
80
+ if args.first.kind_of? Hash
81
+ options = args.first
82
+ else # args is a list of current navigation for several levels
83
+ options = {}
84
+ if args.size == 1 #only an navi-key has been specified, try to find out level
85
+ level = SimpleNavigation.primary_navigation.level_for_item(args.first)
86
+ options[:"level_#{level}"] = args.first if level
87
+ else
88
+ args.each_with_index {|arg, i| options[:"level_#{i + 1}"] = arg}
89
+ end
90
+ end
91
+ #only the deepest level is relevant
92
+ level = options.inject(0) do |max, kv|
93
+ kv.first.to_s =~ /level_(\d)/
94
+ max = $1.to_i if $1.to_i > max
95
+ max
96
+ end
97
+ raise ArgumentError, "Invalid level specified or item key not found" if level == 0
98
+ [level, options[:"level_#{level}"]]
99
+ end
100
+
101
+ end
102
+
103
+ end
104
+
105
+ SimpleNavigation.config_file_path = File.join(RAILS_ROOT, 'config') unless SimpleNavigation.config_file_path
106
+ ActionController::Base.send(:include, SimpleNavigation::ControllerMethods)
107
+ # TODOs for the next releases:
108
+ # 1) add ability to specify explicit highlighting in the config-file itself (directly with the item)
109
+ # - item.highlight_on :controller => 'users', :action => 'show' ...^
110
+ # --> with that we can get rid of the controller_methods...
111
+ #
112
+ # 2) ability to turn off autohighlighting for a single item...
113
+ #
114
+ # 3) add JoinRenderer (HorizontalRenderer?) (wich does not render a list, but just the items joined with a specified char (e.g. | ))
115
+ #
116
+ # 4) Enhance SampleProject (more examples)
117
+ #
118
+ # 5) Make SampleProject public
data/rails/init.rb ADDED
@@ -0,0 +1,3 @@
1
+ default_config_file_path = File.join(RAILS_ROOT, 'config')
2
+ SimpleNavigation.config_file_path = default_config_file_path unless SimpleNavigation.config_file_path
3
+ ActionController::Base.send(:include, SimpleNavigation::ControllerMethods)
@@ -0,0 +1,162 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe SimpleNavigation::Configuration do
4
+
5
+ before(:each) do
6
+ @config = SimpleNavigation::Configuration.instance
7
+ end
8
+
9
+ describe 'self.run' do
10
+ it "should yield the singleton Configuration object" do
11
+ SimpleNavigation::Configuration.run do |c|
12
+ c.should == @config
13
+ end
14
+ end
15
+ end
16
+
17
+ describe 'self.eval_config' do
18
+ before(:each) do
19
+ @context = mock(:context)
20
+ @context.stub!(:instance_eval)
21
+ SimpleNavigation::Configuration.stub!(:context_for_eval => @context)
22
+ @config_files = {:default => 'default', :my_context => 'my_context'}
23
+ SimpleNavigation.stub!(:config_files).and_return(@config_files)
24
+ end
25
+ context "with default navigation context" do
26
+ it "should instance_eval the default config_file-string inside the context" do
27
+ @context.should_receive(:instance_eval).with('default')
28
+ SimpleNavigation::Configuration.eval_config(@context)
29
+ end
30
+ end
31
+ context 'with non default navigation context' do
32
+ it "should instance_eval the specified config_file-string inside the context" do
33
+ @context.should_receive(:instance_eval).with('my_context')
34
+ SimpleNavigation::Configuration.eval_config(@context, :my_context)
35
+ end
36
+ end
37
+ it "should set the controller" do
38
+ @controller = stub(:controller)
39
+ SimpleNavigation::Configuration.should_receive(:extract_controller_from).with(@context).and_return(@controller)
40
+ SimpleNavigation.should_receive(:controller=).with(@controller)
41
+ SimpleNavigation::Configuration.eval_config(@context)
42
+ end
43
+ it "should set the template" do
44
+ @template = stub(:template)
45
+ @controller = stub(:controller, :instance_variable_get => @template)
46
+ SimpleNavigation.stub!(:controller => @controller)
47
+ SimpleNavigation.should_receive(:template=).with(@template)
48
+ SimpleNavigation::Configuration.eval_config(@context)
49
+ end
50
+ end
51
+
52
+ describe 'context_for_eval' do
53
+ context 'controller is present' do
54
+ before(:each) do
55
+ @controller = stub(:controller)
56
+ SimpleNavigation.stub!(:controller => @controller)
57
+ end
58
+ context 'template is present' do
59
+ before(:each) do
60
+ @template = stub(:template)
61
+ SimpleNavigation.stub!(:template => @template)
62
+ end
63
+ it {SimpleNavigation::Configuration.context_for_eval.should == @template}
64
+ end
65
+ context 'template is not present' do
66
+ before(:each) do
67
+ SimpleNavigation.stub!(:template => nil)
68
+ end
69
+ it {SimpleNavigation::Configuration.context_for_eval.should == @controller}
70
+ end
71
+ end
72
+ context 'controller is not present' do
73
+ before(:each) do
74
+ SimpleNavigation.stub!(:controller => nil)
75
+ end
76
+ context 'template is present' do
77
+ before(:each) do
78
+ @template = stub(:template)
79
+ SimpleNavigation.stub!(:template => @template)
80
+ end
81
+ it {SimpleNavigation::Configuration.context_for_eval.should == @template}
82
+ end
83
+ context 'template is not present' do
84
+ before(:each) do
85
+ SimpleNavigation.stub!(:template => nil)
86
+ end
87
+ it {lambda {SimpleNavigation::Configuration.context_for_eval}.should raise_error}
88
+ end
89
+ end
90
+ end
91
+
92
+ describe 'self.extract_controller_from' do
93
+ before(:each) do
94
+ @nav_context = stub(:nav_context)
95
+ end
96
+
97
+ context 'object responds to controller' do
98
+ before(:each) do
99
+ @controller = stub(:controller)
100
+ @nav_context.stub!(:controller).and_return(@controller)
101
+ end
102
+
103
+ it "should return the controller" do
104
+ SimpleNavigation::Configuration.extract_controller_from(@nav_context).should == @controller
105
+ end
106
+
107
+ end
108
+
109
+ context 'object does not respond to controller' do
110
+ it "should return the nav_context" do
111
+ SimpleNavigation::Configuration.extract_controller_from(@nav_context).should == @nav_context
112
+ end
113
+ end
114
+ end
115
+
116
+ describe 'initialize' do
117
+ it "should set the List-Renderer as default upon initialize" do
118
+ @config.renderer.should == SimpleNavigation::Renderer::List
119
+ end
120
+ it "should set the selected_class to 'selected' as default" do
121
+ @config.selected_class.should == 'selected'
122
+ end
123
+ it "should set render_all_levels to false as default" do
124
+ @config.render_all_levels.should be_false
125
+ end
126
+ it "should set autogenerate_item_ids to true as default" do
127
+ @config.autogenerate_item_ids.should be_true
128
+ end
129
+ it "should set auto_highlight to true as default" do
130
+ @config.auto_highlight.should be_true
131
+ end
132
+ end
133
+ describe 'items' do
134
+ before(:each) do
135
+ @container = stub(:items_container)
136
+ SimpleNavigation::ItemContainer.stub!(:new).and_return(@container)
137
+ end
138
+ it "should should yield an new ItemContainer" do
139
+ @config.items do |container|
140
+ container.should == @container
141
+ end
142
+ end
143
+ it "should assign the ItemContainer to an instance-var" do
144
+ @config.items {}
145
+ @config.primary_navigation.should == @container
146
+ end
147
+ end
148
+
149
+ describe 'loaded?' do
150
+ it "should return true if primary_nav is set" do
151
+ @config.instance_variable_set(:@primary_navigation, :bla)
152
+ @config.should be_loaded
153
+ end
154
+ it "should return false if no primary_nav is set" do
155
+ @config.instance_variable_set(:@primary_navigation, nil)
156
+ @config.should_not be_loaded
157
+ end
158
+ end
159
+
160
+ end
161
+
162
+
@@ -0,0 +1,73 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ ActionController::Base.send(:include, SimpleNavigation::ControllerMethods)
4
+
5
+ describe SimpleNavigation::ControllerMethods do
6
+
7
+ def stub_loading_config
8
+ SimpleNavigation::Configuration.stub!(:load)
9
+ end
10
+
11
+ before(:each) do
12
+ stub_loading_config
13
+ class ApplicationController < ActionController::Base
14
+ end
15
+ @controller = ApplicationController.new
16
+ end
17
+
18
+ describe 'when being included' do
19
+ it "should extend the ClassMethods" do
20
+ @controller.class.should respond_to(:navigation)
21
+ end
22
+ it "should include the InstanceMethods" do
23
+ @controller.should respond_to(:current_navigation)
24
+ end
25
+ it "should install the Helpers Module" do
26
+ [:render_navigation, :render_primary_navigation, :render_sub_navigation].each do |m|
27
+ @controller.master_helper_module.instance_methods.map(&:to_s).should include(m.to_s)
28
+ end
29
+ end
30
+ end
31
+
32
+ describe 'class_methods' do
33
+
34
+ describe 'navigation' do
35
+
36
+ def call_navigation(key1, key2=nil)
37
+ @controller.class_eval do
38
+ navigation key1, key2
39
+ end
40
+ end
41
+
42
+ it "should not have an instance-method 'sn_set_navigation' if navigation-method has not been called" do
43
+ @controller.respond_to?(:sn_set_navigation).should be_false
44
+ end
45
+ it 'should create an instance-method "sn_set_navigation" when being called' do
46
+ call_navigation(:key)
47
+ @controller.respond_to?(:sn_set_navigation).should be_true
48
+ end
49
+ it 'the created method should call current_navigation with the specified keys' do
50
+ call_navigation(:primary, :secondary)
51
+ @controller.should_receive(:current_navigation).with(:primary, :secondary)
52
+ @controller.sn_set_navigation
53
+ end
54
+ end
55
+
56
+ end
57
+
58
+ describe 'instance_methods' do
59
+
60
+ describe 'current_navigation' do
61
+ it "should set the sn_current_navigation_args as specified" do
62
+ @controller.current_navigation(:first)
63
+ @controller.instance_variable_get(:@sn_current_navigation_args).should == [:first]
64
+ end
65
+ it "should set the sn_current_navigation_args as specified" do
66
+ @controller.current_navigation(:first, :second)
67
+ @controller.instance_variable_get(:@sn_current_navigation_args).should == [:first, :second]
68
+ end
69
+ end
70
+
71
+ end
72
+
73
+ end
@@ -0,0 +1,125 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe SimpleNavigation::Helpers do
4
+ class ControllerMock
5
+ include SimpleNavigation::Helpers
6
+ end
7
+
8
+ before(:each) do
9
+ @controller = ControllerMock.new
10
+ SimpleNavigation.stub!(:load_config)
11
+ SimpleNavigation::Configuration.stub!(:eval_config)
12
+ @primary_navigation = stub(:primary_navigation, :null_object => true)
13
+ SimpleNavigation.stub!(:primary_navigation).and_return(@primary_navigation)
14
+ end
15
+
16
+ describe 'render_navigation' do
17
+ describe 'regarding loading of the config-file' do
18
+ context 'no options specified' do
19
+ it "should load the config-file for the default context" do
20
+ SimpleNavigation.should_receive(:load_config).with(:default)
21
+ @controller.render_navigation
22
+ end
23
+ end
24
+
25
+ context 'with options specified' do
26
+ it "should load the config-file for the specified context" do
27
+ SimpleNavigation.should_receive(:load_config).with(:my_context)
28
+ @controller.render_navigation(:context => :my_context)
29
+ end
30
+ end
31
+ end
32
+
33
+ it "should eval the config on every request" do
34
+ SimpleNavigation::Configuration.should_receive(:eval_config).with(@controller, :default)
35
+ @controller.render_navigation
36
+ end
37
+
38
+ context 'primary' do
39
+ it "should call render on the primary_navigation" do
40
+ @primary_navigation.should_receive(:render)
41
+ @controller.render_navigation(:primary)
42
+ end
43
+ it "should call render on the primary_navigation (specifying level through options)" do
44
+ @primary_navigation.should_receive(:render)
45
+ @controller.render_navigation(:level => :primary)
46
+ end
47
+ it "should call render on the primary_navigation (specifying level through options)" do
48
+ @primary_navigation.should_receive(:render)
49
+ @controller.render_navigation(:level => 1)
50
+ end
51
+ end
52
+
53
+ context 'secondary' do
54
+ context 'with current_primary_navigation set' do
55
+ before(:each) do
56
+ @selected_item_container = stub(:selected_container, :null_object => true)
57
+ SimpleNavigation.stub!(:active_item_container_for => @selected_item_container)
58
+ end
59
+ it "should find the selected sub_navigation for the specified level" do
60
+ SimpleNavigation.should_receive(:active_item_container_for).with(2)
61
+ @controller.render_navigation(:secondary)
62
+ end
63
+ it "should find the selected sub_navigation for the specified level" do
64
+ SimpleNavigation.should_receive(:active_item_container_for).with(2)
65
+ @controller.render_navigation(:level => :secondary)
66
+ end
67
+ it "should find the selected sub_navigation for the specified level" do
68
+ SimpleNavigation.should_receive(:active_item_container_for).with(1)
69
+ @controller.render_navigation(:level => 1)
70
+ end
71
+ it "should call render on the active item_container" do
72
+ @selected_item_container.should_receive(:render)
73
+ @controller.render_navigation(:secondary)
74
+ end
75
+ end
76
+ context 'without an active item_container set' do
77
+ before(:each) do
78
+ SimpleNavigation.stub!(:active_item_container_for => nil)
79
+ end
80
+ it "should not raise an error" do
81
+ lambda{@controller.render_navigation(:secondary)}.should_not raise_error
82
+ end
83
+ end
84
+
85
+ end
86
+
87
+ context 'nested' do
88
+ it "should call render on the primary navigation with the include_subnavigation option set" do
89
+ @primary_navigation.should_receive(:render).with(true)
90
+ @controller.render_navigation(:nested)
91
+ end
92
+ end
93
+
94
+ context 'unknown level' do
95
+ it "should raise an error" do
96
+ lambda {@controller.render_navigation(:unknown)}.should raise_error(ArgumentError)
97
+ end
98
+ it "should raise an error" do
99
+ lambda {@controller.render_navigation(:level => :unknown)}.should raise_error(ArgumentError)
100
+ end
101
+ it "should raise an error" do
102
+ lambda {@controller.render_navigation('level')}.should raise_error(ArgumentError)
103
+ end
104
+ end
105
+ end
106
+
107
+ describe 'render_primary_navigation' do
108
+ it "should delegate to render_navigation(:primary)" do
109
+ ActiveSupport::Deprecation.silence do
110
+ @controller.should_receive(:render_navigation).with(:level => 1)
111
+ @controller.render_primary_navigation
112
+ end
113
+ end
114
+ end
115
+
116
+ describe 'render_sub_navigation' do
117
+ it "should delegate to render_navigation(:secondary)" do
118
+ ActiveSupport::Deprecation.silence do
119
+ @controller.should_receive(:render_navigation).with(:level => 2)
120
+ @controller.render_sub_navigation
121
+ end
122
+ end
123
+ end
124
+
125
+ end