strikeroff-simple-navigation 2.0.2

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