emenu 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. data/.document +5 -0
  2. data/.rspec +1 -0
  3. data/Gemfile +19 -0
  4. data/Gemfile.lock +71 -0
  5. data/LICENSE.txt +20 -0
  6. data/README.rdoc +103 -0
  7. data/Rakefile +49 -0
  8. data/VERSION +1 -0
  9. data/emenu.gemspec +90 -0
  10. data/lib/emenu.rb +38 -0
  11. data/lib/emenu/menu.rb +93 -0
  12. data/lib/emenu/menu_config.rb +73 -0
  13. data/lib/emenu/string.rb +15 -0
  14. data/lib/generators/emenu/USAGE +1 -0
  15. data/lib/generators/emenu/install_generator.rb +42 -0
  16. data/lib/generators/emenu/templates/default_menu/navigation.rb +33 -0
  17. data/lib/generators/emenu/templates/images/menu.png +0 -0
  18. data/lib/generators/emenu/templates/images/menu_arrow.png +0 -0
  19. data/lib/generators/emenu/templates/images/menu_border.png +0 -0
  20. data/lib/generators/emenu/templates/images/menu_l.png +0 -0
  21. data/lib/generators/emenu/templates/images/menu_l_selected.png +0 -0
  22. data/lib/generators/emenu/templates/images/menu_minus.png +0 -0
  23. data/lib/generators/emenu/templates/images/menu_plus.png +0 -0
  24. data/lib/generators/emenu/templates/images/menu_r.png +0 -0
  25. data/lib/generators/emenu/templates/images/menu_r_selected.png +0 -0
  26. data/lib/generators/emenu/templates/images/menu_selected.png +0 -0
  27. data/lib/generators/emenu/templates/javascripts/emenu.js +71 -0
  28. data/lib/generators/emenu/templates/javascripts/jquery-1.4.2.min.js +154 -0
  29. data/lib/generators/emenu/templates/stylesheets/emenu.css +278 -0
  30. data/lib/generators/emenu/templates/stylesheets/reset.css +53 -0
  31. data/spec/menu_config_spec.rb +149 -0
  32. data/spec/menu_spec.rb +175 -0
  33. data/spec/spec_helper.rb +12 -0
  34. metadata +161 -0
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,19 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+
6
+ # Add dependencies to develop your gem here.
7
+ # Include everything needed to run rake, tests, features, etc.
8
+ #
9
+
10
+ gem "haml", '~> 3.1.1'
11
+ gem "activesupport", '~> 3.0.3'
12
+ gem "actionpack", '~> 3.0.3'
13
+
14
+ group :development do
15
+ gem "rspec-rails", "~> 2.3.0"
16
+ gem "bundler", "~> 1.0.0"
17
+ gem "jeweler", "~> 1.6.4"
18
+ gem "rcov", ">= 0"
19
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,71 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ abstract (1.0.0)
5
+ actionpack (3.0.9)
6
+ activemodel (= 3.0.9)
7
+ activesupport (= 3.0.9)
8
+ builder (~> 2.1.2)
9
+ erubis (~> 2.6.6)
10
+ i18n (~> 0.5.0)
11
+ rack (~> 1.2.1)
12
+ rack-mount (~> 0.6.14)
13
+ rack-test (~> 0.5.7)
14
+ tzinfo (~> 0.3.23)
15
+ activemodel (3.0.9)
16
+ activesupport (= 3.0.9)
17
+ builder (~> 2.1.2)
18
+ i18n (~> 0.5.0)
19
+ activesupport (3.0.9)
20
+ builder (2.1.2)
21
+ diff-lcs (1.1.2)
22
+ erubis (2.6.6)
23
+ abstract (>= 1.0.0)
24
+ git (1.2.5)
25
+ haml (3.1.2)
26
+ i18n (0.5.0)
27
+ jeweler (1.6.4)
28
+ bundler (~> 1.0)
29
+ git (>= 1.2.5)
30
+ rake
31
+ rack (1.2.3)
32
+ rack-mount (0.6.14)
33
+ rack (>= 1.0.0)
34
+ rack-test (0.5.7)
35
+ rack (>= 1.0)
36
+ railties (3.0.9)
37
+ actionpack (= 3.0.9)
38
+ activesupport (= 3.0.9)
39
+ rake (>= 0.8.7)
40
+ rdoc (~> 3.4)
41
+ thor (~> 0.14.4)
42
+ rake (0.9.2)
43
+ rcov (0.9.9)
44
+ rdoc (3.8)
45
+ rspec (2.3.0)
46
+ rspec-core (~> 2.3.0)
47
+ rspec-expectations (~> 2.3.0)
48
+ rspec-mocks (~> 2.3.0)
49
+ rspec-core (2.3.1)
50
+ rspec-expectations (2.3.0)
51
+ diff-lcs (~> 1.1.2)
52
+ rspec-mocks (2.3.0)
53
+ rspec-rails (2.3.1)
54
+ actionpack (~> 3.0)
55
+ activesupport (~> 3.0)
56
+ railties (~> 3.0)
57
+ rspec (~> 2.3.0)
58
+ thor (0.14.6)
59
+ tzinfo (0.3.29)
60
+
61
+ PLATFORMS
62
+ ruby
63
+
64
+ DEPENDENCIES
65
+ actionpack (~> 3.0.3)
66
+ activesupport (~> 3.0.3)
67
+ bundler (~> 1.0.0)
68
+ haml (~> 3.1.1)
69
+ jeweler (~> 1.6.4)
70
+ rcov
71
+ rspec-rails (~> 2.3.0)
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Kresimir Bojcic
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,103 @@
1
+ = Enterprise Menu
2
+
3
+ It extracts logic needed for building effective and maintainable menus for complex enterprise scenarios.
4
+
5
+ Use it if you:
6
+ * have myriad of options to cover
7
+ * have complex business (il)logic that depends on user role
8
+
9
+ Live example can be tried at http://emenu.12ip.net/cars. Open it in a new tab or otherwise prepare to click 'Back' quite a few times.
10
+
11
+ It renders menu based on configuration and persists state with minimal session info and some tiny jQuery posts. Main goal is to hide as much as possible of the HTML/jQuery ugliness from your tender eyes. Evenly important, at least for me, is to make you balls-of-brass-kind-of-guy when customer is asking for yet another exception/addition in menu layout.
12
+
13
+ menu.item :transportation do
14
+ item :cars, cars_path
15
+ item :trains do
16
+ item :fast_trains , fast_trains_path
17
+ item :slow_trains, slow_trains_path
18
+ end
19
+ item :planes do
20
+ item :boeing do
21
+ item :boeing747 do
22
+ item :older_ready_to_crash, older_ready_to_crash_path
23
+ item :brand_new, brand_new_path
24
+ end
25
+ item :boeing474, boeing474_path
26
+ end
27
+ item :airbus, airbus_path
28
+ end
29
+ end
30
+ #second menu group named Orders
31
+ menu.item :orders do
32
+ item :bread, bread_path
33
+ item :butter, butter_path
34
+ item :chunky_bacon, chunky_bacon_path
35
+ end
36
+ end
37
+
38
+ = Supported
39
+ * Unlimited menu levels
40
+ * Separate sets of menu for different roles
41
+ * Exclusion of menu items based on authorisation
42
+
43
+ WARNING: This is release early as it gets so many things are missing. I am planing on releasing often to make this gem worthwhile your time.
44
+
45
+ = Usage
46
+ Put in your Gemfile:
47
+
48
+ gem 'emenu' , :git => 'git@github.com:drKreso/emenu.git'
49
+
50
+ Then type in your console:
51
+
52
+ bundle && rails g emenu:install
53
+
54
+ If you are using Haml, put this in layout:
55
+
56
+ = stylesheet_link_tag 'reset.css', 'emenu.css'
57
+ = javascript_include_tag 'jquery-1.4.2.min.js', 'emenu.js'
58
+ ...
59
+ #content
60
+ #left
61
+ =menu_will_render
62
+
63
+ Otherwise put this in you layout:
64
+
65
+ <%= stylesheet_link_tag 'reset.css', 'emenu.css'%>
66
+ <%= javascript_include_tag 'jquery-1.4.2.min.js', 'emenu.js' %>
67
+ ...
68
+ <div id="content">
69
+ <div id="left">
70
+ <%= menu_will_render %>
71
+ </div>
72
+ </div>
73
+
74
+ Add app/navigation to your autoload paths:
75
+
76
+ config.autoload_paths += %W(#{config.root}/app/navigation)
77
+
78
+ <b>Set up menu configuration in app/navigation/navigation.rb and you are ready to go.</b>
79
+
80
+ == Gotchas
81
+ Theme used is small part of http://themeforest.net/item/smooth-admin/101529. It's just for demonstration and if you plan to use it in your projects you'll have to pay for it. Since you can already steel it from original theme page I don't think I am doing anything wrong including it as a demo. On the other hand the author has not given me permission to do so(yet). All in all it might get a bit uglier if I have to make my own theme...
82
+
83
+ Be aware that I've monkey patched String class with 'indent' method for my testing purposes.
84
+
85
+ == TODO
86
+ * Add quick links support that change according to main menu selection
87
+ * Put authorisation hook that will enable complex authorisation rules for all menu items in one method
88
+
89
+ == Contributing to emenu
90
+
91
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
92
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
93
+ * Fork the project
94
+ * Start a feature/bugfix branch
95
+ * Commit and push until you are happy with your contribution
96
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
97
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
98
+
99
+ == Copyright
100
+
101
+ Copyright (c) 2011 Kresimir Bojcic. See LICENSE.txt for
102
+ further details.
103
+
data/Rakefile ADDED
@@ -0,0 +1,49 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "emenu"
18
+ gem.homepage = "http://github.com/drKreso/emenu"
19
+ gem.license = "MIT"
20
+ gem.summary = %Q{Enterprise class menus}
21
+ gem.description = %Q{Enterprise class menus with simple ruby based configuration, easy to make, easy to maintain}
22
+ gem.email = "kresimir.bojcic@gmail.com"
23
+ gem.authors = ["Kresimir Bojcic"]
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
27
+
28
+ require 'rspec/core'
29
+ require 'rspec/core/rake_task'
30
+ RSpec::Core::RakeTask.new(:spec) do |spec|
31
+ spec.pattern = FileList['spec/**/*_spec.rb']
32
+ end
33
+
34
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
35
+ spec.pattern = 'spec/**/*_spec.rb'
36
+ spec.rcov = true
37
+ end
38
+
39
+ task :default => :spec
40
+
41
+ require 'rake/rdoctask'
42
+ Rake::RDocTask.new do |rdoc|
43
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
44
+
45
+ rdoc.rdoc_dir = 'rdoc'
46
+ rdoc.title = "emenu #{version}"
47
+ rdoc.rdoc_files.include('README*')
48
+ rdoc.rdoc_files.include('lib/**/*.rb')
49
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.4.1
data/emenu.gemspec ADDED
@@ -0,0 +1,90 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{emenu}
8
+ s.version = "0.4.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = [%q{Kresimir Bojcic}]
12
+ s.date = %q{2011-07-13}
13
+ s.description = %q{Enterprise class menus with simple ruby based configuration, easy to make, easy to maintain}
14
+ s.email = %q{kresimir.bojcic@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".rspec",
22
+ "Gemfile",
23
+ "Gemfile.lock",
24
+ "LICENSE.txt",
25
+ "README.rdoc",
26
+ "Rakefile",
27
+ "VERSION",
28
+ "emenu.gemspec",
29
+ "lib/emenu.rb",
30
+ "lib/emenu/menu.rb",
31
+ "lib/emenu/menu_config.rb",
32
+ "lib/emenu/string.rb",
33
+ "lib/generators/emenu/USAGE",
34
+ "lib/generators/emenu/install_generator.rb",
35
+ "lib/generators/emenu/templates/default_menu/navigation.rb",
36
+ "lib/generators/emenu/templates/images/menu.png",
37
+ "lib/generators/emenu/templates/images/menu_arrow.png",
38
+ "lib/generators/emenu/templates/images/menu_border.png",
39
+ "lib/generators/emenu/templates/images/menu_l.png",
40
+ "lib/generators/emenu/templates/images/menu_l_selected.png",
41
+ "lib/generators/emenu/templates/images/menu_minus.png",
42
+ "lib/generators/emenu/templates/images/menu_plus.png",
43
+ "lib/generators/emenu/templates/images/menu_r.png",
44
+ "lib/generators/emenu/templates/images/menu_r_selected.png",
45
+ "lib/generators/emenu/templates/images/menu_selected.png",
46
+ "lib/generators/emenu/templates/javascripts/emenu.js",
47
+ "lib/generators/emenu/templates/javascripts/jquery-1.4.2.min.js",
48
+ "lib/generators/emenu/templates/stylesheets/emenu.css",
49
+ "lib/generators/emenu/templates/stylesheets/reset.css",
50
+ "spec/menu_config_spec.rb",
51
+ "spec/menu_spec.rb",
52
+ "spec/spec_helper.rb"
53
+ ]
54
+ s.homepage = %q{http://github.com/drKreso/emenu}
55
+ s.licenses = [%q{MIT}]
56
+ s.require_paths = [%q{lib}]
57
+ s.rubygems_version = %q{1.8.5}
58
+ s.summary = %q{Enterprise class menus}
59
+
60
+ if s.respond_to? :specification_version then
61
+ s.specification_version = 3
62
+
63
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
64
+ s.add_runtime_dependency(%q<haml>, ["~> 3.1.1"])
65
+ s.add_runtime_dependency(%q<activesupport>, ["~> 3.0.3"])
66
+ s.add_runtime_dependency(%q<actionpack>, ["~> 3.0.3"])
67
+ s.add_development_dependency(%q<rspec-rails>, ["~> 2.3.0"])
68
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
69
+ s.add_development_dependency(%q<jeweler>, ["~> 1.6.4"])
70
+ s.add_development_dependency(%q<rcov>, [">= 0"])
71
+ else
72
+ s.add_dependency(%q<haml>, ["~> 3.1.1"])
73
+ s.add_dependency(%q<activesupport>, ["~> 3.0.3"])
74
+ s.add_dependency(%q<actionpack>, ["~> 3.0.3"])
75
+ s.add_dependency(%q<rspec-rails>, ["~> 2.3.0"])
76
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
77
+ s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
78
+ s.add_dependency(%q<rcov>, [">= 0"])
79
+ end
80
+ else
81
+ s.add_dependency(%q<haml>, ["~> 3.1.1"])
82
+ s.add_dependency(%q<activesupport>, ["~> 3.0.3"])
83
+ s.add_dependency(%q<actionpack>, ["~> 3.0.3"])
84
+ s.add_dependency(%q<rspec-rails>, ["~> 2.3.0"])
85
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
86
+ s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
87
+ s.add_dependency(%q<rcov>, [">= 0"])
88
+ end
89
+ end
90
+
data/lib/emenu.rb ADDED
@@ -0,0 +1,38 @@
1
+ require 'action_view'
2
+
3
+ require 'emenu/string.rb'
4
+ require 'emenu/menu.rb'
5
+ require 'emenu/menu_config.rb'
6
+
7
+ module Emenu
8
+
9
+
10
+ def emenu_state
11
+ session[:menu_state] ||= {}
12
+ session[:menu_state][params[:id]] = params[:state] unless params[:id].nil?
13
+ render :json => { :data => :ok }
14
+ end
15
+
16
+ def emenu
17
+ session[:menu_state] ||= {}
18
+ @menu = menu
19
+ selected_key = @menu.find_path(request.env['PATH_INFO'])
20
+ unless selected_key.nil?
21
+ @menu.selected = selected_key
22
+ @menu.find(selected_key).parents.each { |menu| session[:menu_state][menu.title.to_s] = "opened" }
23
+ end
24
+ session[:menu_state].each { |key, value| @menu.open(key.to_sym) if value == "opened"} unless session[:menu_state].nil?
25
+ end
26
+
27
+ def self.included(base)
28
+ base.before_filter :emenu
29
+ base.send :helper_method, :menu_will_render
30
+ base.module_eval { include Navigation } unless $install == true
31
+ end
32
+
33
+ def menu_will_render
34
+ @menu.render
35
+ end
36
+
37
+ end
38
+
data/lib/emenu/menu.rb ADDED
@@ -0,0 +1,93 @@
1
+ class Menu
2
+
3
+ IDENTATION = 2
4
+ SPACE = " "
5
+ DEFAULT_SPACE = SPACE*IDENTATION
6
+
7
+ attr_reader :title, :items, :type, :path
8
+
9
+ def initialize(title, parent, config, path="#")
10
+ @title = title
11
+ @parent = parent
12
+ @config = config
13
+ @path = path
14
+ @items = []
15
+ @type = @parent.nil? ? :title : :menu
16
+ end
17
+
18
+ def item(name, path="#", &block)
19
+ current = self.add(name,path)
20
+ current.instance_eval(&block) unless block.nil?
21
+ end
22
+
23
+ def add(item_name, path="#", &block)
24
+ items << Menu.new(item_name, self, @config, path)
25
+ @type = :collapsible_menu if @type == :menu # when we add subitems to menu it becomes collapsible menu
26
+ instance_eval(&block) unless block.nil?
27
+ MenuConfig.enforce_uniqueness names
28
+ return items.last
29
+ end
30
+
31
+ def names
32
+ items.inject([@title.to_s]) { |result, item| result << item.names }
33
+ end
34
+
35
+ def to_haml
36
+ items.inject(eval "#{type}_haml") { |result, item| result << item.to_haml }
37
+ end
38
+
39
+ def title_haml
40
+ "#{spacing}%h6#h-menu-#{@title}#{opened? ? '{ :class => "selected" }' : ""}\n" <<
41
+ "#{spacing_subitem}%a{ :href => '##{@title}'}\n" <<
42
+ "#{spacing_subitem*2}%span #{humanized_title}\n" <<
43
+ "%ul#menu-#{@title}.#{opened? ? "opened" : "closed"}\n"
44
+ end
45
+
46
+ def menu_haml
47
+ "#{spacing}%li#{selected? ? '{ :class => "selected" }' : ""}= link_to '#{humanized_title}', '#{@path}'\n"
48
+ end
49
+
50
+ def collapsible_menu_haml
51
+ "#{spacing}%li.collapsible\n" <<
52
+ "#{spacing_subitem}%a{ :href => '#', :class => 'collapsible #{opened? ? "minus" : "plus"}'} #{humanized_title}\n" <<
53
+ "#{spacing_subitem}%ul##{@title}.#{opened? ? "expanded" : "collapsed"}\n"
54
+ end
55
+
56
+ def humanized_title
57
+ @title.to_s.humanize
58
+ end
59
+
60
+ def selected?
61
+ @config.selected == @title
62
+ end
63
+
64
+ def opened?
65
+ @config.opened? @title
66
+ end
67
+
68
+ def indent
69
+ result = @parent.nil? ? 0 : @parent.indent + IDENTATION * (@parent.type == :collapsible_menu ? 2 : 1)
70
+ end
71
+
72
+ def spacing
73
+ SPACE*indent
74
+ end
75
+
76
+ def spacing_subitem
77
+ spacing + DEFAULT_SPACE
78
+ end
79
+
80
+ def all_items
81
+ items.inject([self]) { |result, menu| result << menu.all_items }
82
+ end
83
+
84
+ def parents
85
+ result = []
86
+ unless @parent.nil?
87
+ result << @parent
88
+ result << @parent.parents unless @parent.parents == []
89
+ end
90
+ return result.flatten
91
+ end
92
+
93
+ end