mmmenu 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +8 -0
- data/Gemfile.lock +26 -0
- data/README.markdown +140 -0
- data/Rakefile +30 -0
- data/VERSION +1 -0
- data/lib/generators/mmmenu/mmmenu_generator.rb +14 -0
- data/lib/generators/templates/helpers/mmmenu_helper.rb +16 -0
- data/lib/generators/templates/views/mmmenu/_current_item.erb +1 -0
- data/lib/generators/templates/views/mmmenu/_item.erb +1 -0
- data/lib/generators/templates/views/mmmenu/_level_1.erb +1 -0
- data/lib/generators/templates/views/mmmenu/_level_2.erb +1 -0
- data/lib/mmmenu.rb +163 -0
- data/lib/mmmenu/engine.rb +16 -0
- data/lib/mmmenu/level.rb +24 -0
- data/lib/mmmenu/version.rb +3 -0
- data/mmmenu.gemspec +63 -0
- data/spec/lib/mmmenu_level_spec.rb +21 -0
- data/spec/lib/mmmenu_spec.rb +155 -0
- data/spec/spec_helper.rb +0 -0
- metadata +102 -0
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
diff-lcs (1.1.3)
|
5
|
+
git (1.2.5)
|
6
|
+
jeweler (1.6.4)
|
7
|
+
bundler (~> 1.0)
|
8
|
+
git (>= 1.2.5)
|
9
|
+
rake
|
10
|
+
rake (0.9.2.2)
|
11
|
+
rspec (2.8.0)
|
12
|
+
rspec-core (~> 2.8.0)
|
13
|
+
rspec-expectations (~> 2.8.0)
|
14
|
+
rspec-mocks (~> 2.8.0)
|
15
|
+
rspec-core (2.8.0)
|
16
|
+
rspec-expectations (2.8.0)
|
17
|
+
diff-lcs (~> 1.1.2)
|
18
|
+
rspec-mocks (2.8.0)
|
19
|
+
|
20
|
+
PLATFORMS
|
21
|
+
ruby
|
22
|
+
|
23
|
+
DEPENDENCIES
|
24
|
+
bundler (~> 1.0.0)
|
25
|
+
jeweler (~> 1.6.4)
|
26
|
+
rspec (~> 2.8.0)
|
data/README.markdown
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
Mmmenu
|
2
|
+
======
|
3
|
+
|
4
|
+
*Flexible menu generator for rails*
|
5
|
+
|
6
|
+
Why another menu plugin?
|
7
|
+
------------------------
|
8
|
+
All menu plugins I've seen have HTML markup hardcoded into them.
|
9
|
+
*Mmmenu* offers you a chance to define your own markup, along with
|
10
|
+
a nice DSL to describe multi-level menu structures.
|
11
|
+
|
12
|
+
INSTALLATION
|
13
|
+
------------
|
14
|
+
|
15
|
+
1. gem install mmmenu
|
16
|
+
2. rails generate mmmenu (optional)
|
17
|
+
|
18
|
+
Basic Usage
|
19
|
+
---------------
|
20
|
+
|
21
|
+
Imagine you put this into your controller:
|
22
|
+
|
23
|
+
mmmenu do |l1|
|
24
|
+
|
25
|
+
l1.add "Articles", "/articles" do |l2|
|
26
|
+
l2.add "Create article", new_article_path
|
27
|
+
l2.add "Articles authors", "/articles/authors", :match_subpaths => true
|
28
|
+
end
|
29
|
+
l1.add "Item2", "/path2"
|
30
|
+
l1.add "Item3", "/path3"
|
31
|
+
l1.add "Item4", "/path4"
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
As you can see, we specify the paths, so our menu does not depend on the routes.
|
36
|
+
`#mmmenu` method automatically puts your menu into @menu instance variable. If you wish to use another variable,
|
37
|
+
you may use a more explicit syntax:
|
38
|
+
|
39
|
+
@my_menu = Mmmenu.new(:request => request) do |l1| { ... }
|
40
|
+
|
41
|
+
Now let's see what happens in the views:
|
42
|
+
|
43
|
+
<%= build_mmmenu(@menu) %>
|
44
|
+
|
45
|
+
And that's it, you get your menu rendered.
|
46
|
+
|
47
|
+
Customizing Views
|
48
|
+
------------------------
|
49
|
+
Now, like I promised, the html-markup is totally
|
50
|
+
configurable.
|
51
|
+
|
52
|
+
Run `rails generate mmmenu`, you'll get your app/helpers/mmmenu_helper.rb file and a bunch of templates copied out of the plugin views/mmmenu directory into app/views/mmmenu directory, thus replacing the plugin default files. Here's what those template files are and what they mean:
|
53
|
+
|
54
|
+
`_current_item.erb` is responsible for the current item in the menu
|
55
|
+
`_item.erb` is responsible for the non-current items
|
56
|
+
`level_1.erb` is a wrapper for menu level 1
|
57
|
+
`level_2.erb` is a wrapper for menu level 2 (submenu)
|
58
|
+
|
59
|
+
If you wish to customize deeper levels of menus and items in them, you should take a look at the generated `mmmenu_helper.rb` file
|
60
|
+
|
61
|
+
Customizing the Helper
|
62
|
+
----------------------------
|
63
|
+
|
64
|
+
Let's take a look at what's inside this helper:
|
65
|
+
|
66
|
+
def build_mmmenu(menu)
|
67
|
+
menu.item_markup(1) do |link, text, options|
|
68
|
+
render(:partial => "mmmenu/item", :locals => { :link => link, :text => text, :options => options })
|
69
|
+
end
|
70
|
+
menu.current_item_markup(1) do |link, text, options|
|
71
|
+
render(:partial => "mmmenu/current_item", :locals => { :link => link, :text => text, :options => options })
|
72
|
+
end
|
73
|
+
menu.level_markup(1) { |m| render(:partial => "mmmenu/level_1", :locals => { :menu => m }) }
|
74
|
+
menu.level_markup(2) { |m| render(:partial => "mmmenu/level_2", :locals => { :menu => m }) }
|
75
|
+
menu.build.html_safe
|
76
|
+
end
|
77
|
+
|
78
|
+
You can see now, that `#item_markup` method defines the html markup for menu item,
|
79
|
+
and `#level_markup` does the same for menu level wrapper. The first argument is the menu level.
|
80
|
+
You may define as much levels as you want, but you don't need to define markup for each level: the deepest level of the markup
|
81
|
+
defined will be used for all of the deeper levels.
|
82
|
+
|
83
|
+
By default, build_mmmenu helper defines two partial templates for level 1 of the menu.
|
84
|
+
It does so by calling two methods on a Mmmenu object:
|
85
|
+
#current_item_markup defines markup for the current menu item
|
86
|
+
#item_markup defines markup for all other menu items
|
87
|
+
Both methods accept an optional second argument - a hash of html_options:
|
88
|
+
|
89
|
+
menu.item_markup(1, :class => "mmmenu")
|
90
|
+
|
91
|
+
which is later used in the templates like this:
|
92
|
+
|
93
|
+
<li><%= link_to text, link, options %></li>
|
94
|
+
|
95
|
+
Note, that this is an example from a default template and options hash may not be present in the customized template.
|
96
|
+
|
97
|
+
Disclaimer: if you call Mmmenu#item_markup for a certain level, you MUST call Mmmenu#current_item_markup
|
98
|
+
for the same level.
|
99
|
+
|
100
|
+
Most of the time, you will want to customize your views, not the helper, so you may as well delete it from your application/helpers dir.
|
101
|
+
|
102
|
+
|
103
|
+
Finally, let's take a closer look at some of the options and what they mean.
|
104
|
+
---------------------------------------------------------------------
|
105
|
+
##### Current item
|
106
|
+
Mmmenu automatically marks each current menu_item with the markup, that you provide in `#current_item_markup` method's block. The item is considered current not only if the paths
|
107
|
+
match, but also if one of the children of the item is marked as current. You may as well set the current item manually like that:
|
108
|
+
|
109
|
+
@menu.current_item = '/articles'
|
110
|
+
|
111
|
+
In this case `'/articles'` would match against the second argument that you pass to the `#add` method, which is the path the menu itme points to.
|
112
|
+
Note that unless the item with such path is not present in the menu, then no item will be makred as current. It is a useful technique when you want
|
113
|
+
to prevent the item from being current in the controller. For example:
|
114
|
+
|
115
|
+
@menu.current_item = '/search-results-pertending-to-be-a-list' if search_query_empty?
|
116
|
+
|
117
|
+
##### Paths
|
118
|
+
For each menu item you may specify a number of paths, that should match for the item to be active.
|
119
|
+
Unless you provide the `:path` option for `Mmmenu#add`, the second argument is used as the matching path.
|
120
|
+
If you'd like to specify paths explicitly, do something like this:
|
121
|
+
|
122
|
+
`l1.add "Articles" articles_path, :paths => [[new_article_path, 'get'], [articles_path, 'post'], [articles_path, 'get']]`
|
123
|
+
|
124
|
+
This way, the menu item will appear active even when you're on the /articles/new page.
|
125
|
+
There's also a third array element, which must be hash. In it may list request_params that should match, for example:
|
126
|
+
|
127
|
+
`l1.add "Articles" articles_path, :paths => [[articles_path, 'get', {:filter => 'published'}]]`
|
128
|
+
|
129
|
+
That way, only a request to "/articles?filter=published" will make this menu item active.
|
130
|
+
Of course it doesn't matter, if the request contains some other params. But you can make sure it doesn't by saying something like `{:personal => nil}`
|
131
|
+
|
132
|
+
Alernatively, you can do this:
|
133
|
+
|
134
|
+
l1.add "Articles" articles_path, :match_subpaths => true
|
135
|
+
|
136
|
+
Or you may use wildcards:
|
137
|
+
|
138
|
+
l1.add "Articles" articles_path, :paths => [["/articles/*"]]
|
139
|
+
|
140
|
+
That's much easier usually.
|
data/Rakefile
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
require 'rspec/core/rake_task'
|
6
|
+
|
7
|
+
begin
|
8
|
+
Bundler.setup(:default, :development)
|
9
|
+
rescue Bundler::BundlerError => e
|
10
|
+
$stderr.puts e.message
|
11
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
12
|
+
exit e.status_code
|
13
|
+
end
|
14
|
+
require 'rake'
|
15
|
+
|
16
|
+
require 'jeweler'
|
17
|
+
Jeweler::Tasks.new do |gem|
|
18
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
19
|
+
gem.name = "mmmenu"
|
20
|
+
gem.homepage = "http://github.com/snitko/mmmenu"
|
21
|
+
gem.license = "MIT"
|
22
|
+
gem.summary = %Q{Flexible menu generator for Rails.}
|
23
|
+
gem.description = %Q{Defines multilevel menu structures, uses custom html templates to render them.}
|
24
|
+
gem.email = "roman.snitko@gmail.com"
|
25
|
+
gem.authors = ["Roman Snitko"]
|
26
|
+
# dependencies defined in Gemfile
|
27
|
+
end
|
28
|
+
Jeweler::RubygemsDotOrgTasks.new
|
29
|
+
|
30
|
+
RSpec::Core::RakeTask.new(:default)
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.5.0
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class MmmenuGenerator < Rails::Generators::Base
|
2
|
+
|
3
|
+
source_root File.expand_path("../../templates", __FILE__)
|
4
|
+
|
5
|
+
def create_helper
|
6
|
+
copy_file "helpers/mmmenu_helper.rb", "app/helpers/mmmenu_helper.rb"
|
7
|
+
copy_file "views/mmmenu/_item.erb", "app/views/mmmenu/_item.erb"
|
8
|
+
copy_file "views/mmmenu/_current_item.erb", "app/views/mmmenu/_current_item.erb"
|
9
|
+
copy_file "views/mmmenu/_level_1.erb", "app/views/mmmenu/_level_1.erb"
|
10
|
+
copy_file "views/mmmenu/_level_2.erb", "app/views/mmmenu/_level_2.erb"
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module MmmenuHelper
|
2
|
+
|
3
|
+
def build_mmmenu(menu)
|
4
|
+
return nil unless menu
|
5
|
+
menu.item_markup(1) do |link, text, options|
|
6
|
+
render(:partial => "mmmenu/item", :locals => { :link => link, :text => text, :options => options })
|
7
|
+
end
|
8
|
+
menu.current_item_markup(1) do |link, text, options|
|
9
|
+
render(:partial => "mmmenu/current_item", :locals => { :link => link, :text => text, :options => options })
|
10
|
+
end
|
11
|
+
menu.level_markup(1) { |m| render(:partial => "mmmenu/level_1", :locals => { :menu => m }) }
|
12
|
+
menu.level_markup(2) { |m| render(:partial => "mmmenu/level_2", :locals => { :menu => m }) }
|
13
|
+
menu.build.html_safe
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
<li><%= link_to text, link, options.merge({:class => "current"}) %></li>
|
@@ -0,0 +1 @@
|
|
1
|
+
<li><%= link_to text, link, options %></li>
|
@@ -0,0 +1 @@
|
|
1
|
+
<ul class="menu"><%=raw menu %></ul>
|
@@ -0,0 +1 @@
|
|
1
|
+
<ul class="submenu"><%=raw menu %></ul>
|
data/lib/mmmenu.rb
ADDED
@@ -0,0 +1,163 @@
|
|
1
|
+
require 'mmmenu/level'
|
2
|
+
require 'mmmenu/engine'
|
3
|
+
|
4
|
+
class Mmmenu
|
5
|
+
|
6
|
+
attr_accessor :current_item
|
7
|
+
|
8
|
+
def initialize(options, &block)
|
9
|
+
@items = options[:items] || Mmmenu::Level.new(&block).to_a
|
10
|
+
@current_path = options[:request].path.chomp('/')
|
11
|
+
@request_params = options[:request].params
|
12
|
+
@request_type = options[:request].method.to_s.downcase
|
13
|
+
@item_markup = []
|
14
|
+
@current_item_markup = []
|
15
|
+
@level_markup = []
|
16
|
+
end
|
17
|
+
|
18
|
+
# The following two methods define
|
19
|
+
# the markup for each menu item on the current and
|
20
|
+
# lower levels (unless lower levels are not redefined later).
|
21
|
+
# Example:
|
22
|
+
# @menu.item_markup(0, options) do |link, text, options|
|
23
|
+
# "<a href=\"#{link}\" #{options}>#{text}</a>"
|
24
|
+
# end
|
25
|
+
def item_markup(level, options={}, &block)
|
26
|
+
level -= 1
|
27
|
+
@item_markup[level] = { :block => block, :options => options }
|
28
|
+
end
|
29
|
+
def current_item_markup(level, options={}, &block)
|
30
|
+
level -= 1
|
31
|
+
@current_item_markup[level] = { :block => block, :options => options }
|
32
|
+
end
|
33
|
+
|
34
|
+
# Defines the markup wrapper for the current menu level and
|
35
|
+
# lower menu levels (unless lower levels are not redefined later).
|
36
|
+
# Example:
|
37
|
+
# @menu.level_markup(0) { |level_content| "<div>#{level_content}</div>" }
|
38
|
+
def level_markup(level=1, &block)
|
39
|
+
level -= 1
|
40
|
+
@level_markup[level] = block
|
41
|
+
end
|
42
|
+
|
43
|
+
def build
|
44
|
+
build_level[:output]
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def build_level(items=@items, level=0)
|
50
|
+
|
51
|
+
item_markup = build_item_markup(level)
|
52
|
+
level_markup = build_level_markup(level)
|
53
|
+
|
54
|
+
# Parsing of a single menu level happens here
|
55
|
+
output = ''
|
56
|
+
has_current_item = false
|
57
|
+
|
58
|
+
raise("Mmmenu object #{self} is empty, no items defined!") if items.nil? or items.empty?
|
59
|
+
items.each do |item|
|
60
|
+
|
61
|
+
item[:html_options] = {} unless item[:html_options]
|
62
|
+
child_menu = build_level(item[:children], level+1) if item[:children]
|
63
|
+
child_output = child_menu[:output] if child_menu
|
64
|
+
|
65
|
+
#############################################################
|
66
|
+
# Here's where we check if the current item is a current item
|
67
|
+
# and we should use a special markup for it
|
68
|
+
#############################################################
|
69
|
+
if (
|
70
|
+
item[:href] == current_item or
|
71
|
+
( current_item.nil? && (
|
72
|
+
item_paths_match?(item) or
|
73
|
+
(child_menu and child_menu[:has_current_item]) or
|
74
|
+
item_href_match?(item)
|
75
|
+
))
|
76
|
+
) && !has_current_item
|
77
|
+
|
78
|
+
then
|
79
|
+
has_current_item = true
|
80
|
+
item_output = item_markup[:current][:block].call(item[:href], item[:title], item_markup[:current][:options].merge(item[:html_options]))
|
81
|
+
else
|
82
|
+
item_output = item_markup[:basic][:block].call(item[:href], item[:title], item_markup[:basic][:options].merge(item[:html_options]))
|
83
|
+
end
|
84
|
+
#############################################################
|
85
|
+
|
86
|
+
output += "#{item_output}#{child_output}"
|
87
|
+
end
|
88
|
+
|
89
|
+
output = level_markup.call(output)
|
90
|
+
{ :has_current_item => has_current_item, :output => output }
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
# Matches menu item against :paths option
|
96
|
+
def item_paths_match?(item)
|
97
|
+
if item[:paths]
|
98
|
+
|
99
|
+
item[:paths].each do |path|
|
100
|
+
if path.kind_of?(Array)
|
101
|
+
# IF path matches perfectly
|
102
|
+
request_type_option = path[1] || ""
|
103
|
+
if ((@current_path == path[0].chomp('/') and @request_type == request_type_option.downcase) or
|
104
|
+
# OR IF * wildcard is used and path matches
|
105
|
+
(path[0] =~ /\*$/ and @current_path =~ /^#{path[0].chomp('*')}(.+)?$/)) and
|
106
|
+
# all listed request params match
|
107
|
+
params_match?(path)
|
108
|
+
return true
|
109
|
+
end
|
110
|
+
else
|
111
|
+
return true if @current_path == path
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
return false
|
117
|
+
end
|
118
|
+
|
119
|
+
# Matches menu item against the actual path it's pointing to.
|
120
|
+
# Is only applied when :path option is not present.
|
121
|
+
def item_href_match?(item)
|
122
|
+
if item[:href]
|
123
|
+
item_href = item[:href].chomp('/')
|
124
|
+
if (@current_path == item_href) or # IF path matches perfectly
|
125
|
+
(item[:match_subpaths] and @current_path =~ /^#{item_href}(\/.+)?$/) # OR IF :match_subpaths options is used and path matches
|
126
|
+
return true
|
127
|
+
end
|
128
|
+
end unless item[:paths]
|
129
|
+
return false
|
130
|
+
end
|
131
|
+
|
132
|
+
def build_item_markup(level)
|
133
|
+
if @item_markup[level]
|
134
|
+
{ :basic => @item_markup[level], :current => @current_item_markup[level] }
|
135
|
+
else
|
136
|
+
unless @item_markup.empty?
|
137
|
+
{ :basic => @item_markup.last, :current => @current_item_markup.last }
|
138
|
+
else
|
139
|
+
{ :basic => {:block => lambda { |link,text,options| "#{text} #{link} #{options}\n" }, :options => {} }, :current => { :block => lambda { |link,text,options| "#{text} #{link} #{options} current\n" }, :options => {} } }
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def build_level_markup(level)
|
145
|
+
if @level_markup[level]
|
146
|
+
@level_markup[level]
|
147
|
+
else
|
148
|
+
if @level_markup.empty?
|
149
|
+
lambda { |menu| menu }
|
150
|
+
else
|
151
|
+
@level_markup.last
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def params_match?(path)
|
157
|
+
path[2].each do |k,v|
|
158
|
+
return false unless (@request_params[k.to_s].nil? and v.nil?) or @request_params[k.to_s] == v.to_s
|
159
|
+
end if path[2]
|
160
|
+
true
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class Mmmenu::Engine < Rails::Engine
|
2
|
+
|
3
|
+
paths["app/helpers"] << File.expand_path("../../generators/templates/helpers", __FILE__)
|
4
|
+
paths["app/views"] << File.expand_path("../../generators/templates/views", __FILE__)
|
5
|
+
|
6
|
+
end if defined?(Rails::Engine)
|
7
|
+
|
8
|
+
ActionController::Base.class_eval do
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def mmmenu(&block)
|
13
|
+
@menu = Mmmenu.new(:request => request, &block)
|
14
|
+
end
|
15
|
+
|
16
|
+
end if defined?(ActionController::Base)
|
data/lib/mmmenu/level.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
class Mmmenu
|
2
|
+
|
3
|
+
class Level
|
4
|
+
|
5
|
+
def initialize(&block)
|
6
|
+
@items = []
|
7
|
+
yield(self)
|
8
|
+
end
|
9
|
+
|
10
|
+
def add(title, href, options={}, &block)
|
11
|
+
children = {}
|
12
|
+
if block_given? # which means current item has children
|
13
|
+
children = { :children => self.class.new(&block).to_a }
|
14
|
+
end
|
15
|
+
@items << { :title => title, :href => href }.merge(options).merge(children)
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_a
|
19
|
+
@items
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
data/mmmenu.gemspec
ADDED
@@ -0,0 +1,63 @@
|
|
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 = "mmmenu"
|
8
|
+
s.version = "0.5.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Roman Snitko"]
|
12
|
+
s.date = "2012-02-27"
|
13
|
+
s.description = "Defines multilevel menu structures, uses custom html templates to render them."
|
14
|
+
s.email = "roman.snitko@gmail.com"
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"README.markdown"
|
17
|
+
]
|
18
|
+
s.files = [
|
19
|
+
"Gemfile",
|
20
|
+
"Gemfile.lock",
|
21
|
+
"README.markdown",
|
22
|
+
"Rakefile",
|
23
|
+
"VERSION",
|
24
|
+
"lib/generators/mmmenu/mmmenu_generator.rb",
|
25
|
+
"lib/generators/templates/helpers/mmmenu_helper.rb",
|
26
|
+
"lib/generators/templates/views/mmmenu/_current_item.erb",
|
27
|
+
"lib/generators/templates/views/mmmenu/_item.erb",
|
28
|
+
"lib/generators/templates/views/mmmenu/_level_1.erb",
|
29
|
+
"lib/generators/templates/views/mmmenu/_level_2.erb",
|
30
|
+
"lib/mmmenu.rb",
|
31
|
+
"lib/mmmenu/engine.rb",
|
32
|
+
"lib/mmmenu/level.rb",
|
33
|
+
"lib/mmmenu/version.rb",
|
34
|
+
"mmmenu.gemspec",
|
35
|
+
"spec/lib/mmmenu_level_spec.rb",
|
36
|
+
"spec/lib/mmmenu_spec.rb",
|
37
|
+
"spec/spec_helper.rb"
|
38
|
+
]
|
39
|
+
s.homepage = "http://github.com/snitko/mmmenu"
|
40
|
+
s.licenses = ["MIT"]
|
41
|
+
s.require_paths = ["lib"]
|
42
|
+
s.rubygems_version = "1.8.10"
|
43
|
+
s.summary = "Flexible menu generator for Rails."
|
44
|
+
|
45
|
+
if s.respond_to? :specification_version then
|
46
|
+
s.specification_version = 3
|
47
|
+
|
48
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
49
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
50
|
+
s.add_development_dependency(%q<rspec>, ["~> 2.8.0"])
|
51
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.6.4"])
|
52
|
+
else
|
53
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
54
|
+
s.add_dependency(%q<rspec>, ["~> 2.8.0"])
|
55
|
+
s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
|
56
|
+
end
|
57
|
+
else
|
58
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
59
|
+
s.add_dependency(%q<rspec>, ["~> 2.8.0"])
|
60
|
+
s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../lib/mmmenu/level')
|
2
|
+
|
3
|
+
describe Mmmenu::Level do
|
4
|
+
|
5
|
+
it "builds a menu level hash representation" do
|
6
|
+
@menu_level = Mmmenu::Level.new do |l1|
|
7
|
+
l1.add "Title1", "/path1"
|
8
|
+
l1.add "Title2", "/path2" do |l2|
|
9
|
+
l2.add "SubTitle", "/path2/subpath"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
@menu_level.to_a.should == [
|
14
|
+
{:title => "Title1", :href => "/path1"},
|
15
|
+
{:title => "Title2", :href => "/path2", :children => [
|
16
|
+
{ :title => "SubTitle", :href => "/path2/subpath" }
|
17
|
+
]}
|
18
|
+
]
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../lib/mmmenu/level')
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../lib/mmmenu')
|
3
|
+
|
4
|
+
describe Mmmenu do
|
5
|
+
|
6
|
+
before(:all) do
|
7
|
+
@items = [
|
8
|
+
{ :name => :numbers, :title => 'Item1', :children => [
|
9
|
+
{ :title => 'Create', :href => '/items1/new', :paths => ['/items1/new'] },
|
10
|
+
{ :title => 'Index', :href => '/items1/' },
|
11
|
+
{ :title => 'Print', :href => '/items1/print' }
|
12
|
+
]},
|
13
|
+
{ :title => 'Item2', :href => '/items2' },
|
14
|
+
{ :title => 'Item3', :href => '/items3' },
|
15
|
+
{ :title => 'Item4', :href => '/items4' }
|
16
|
+
]
|
17
|
+
end
|
18
|
+
|
19
|
+
before(:each) do
|
20
|
+
@request = mock('request')
|
21
|
+
@request.stub!(:path).once.and_return('/items1/new')
|
22
|
+
@request.stub!(:method).once.and_return('get')
|
23
|
+
@request.stub!(:params).once.and_return({})
|
24
|
+
@menu = Mmmenu.new(:items => @items, :request => @request )
|
25
|
+
end
|
26
|
+
|
27
|
+
it "renders one level" do
|
28
|
+
set_menu_markup
|
29
|
+
@menu.item_markup(2) do |link, text, options|
|
30
|
+
" #{text}: #{link}\n"
|
31
|
+
end
|
32
|
+
@menu.current_item_markup(2) do |link, text, options|
|
33
|
+
" #{text}: #{link} current\n"
|
34
|
+
end
|
35
|
+
@menu.level_markup(1) { |menu| menu }
|
36
|
+
@menu.build.should == <<END
|
37
|
+
Item1: current
|
38
|
+
Create: /items1/new current
|
39
|
+
Index: /items1/
|
40
|
+
Print: /items1/print
|
41
|
+
Item2: /items2
|
42
|
+
Item3: /items3
|
43
|
+
Item4: /items4
|
44
|
+
END
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
it "chooses the current item depending on request type" do
|
49
|
+
items = [
|
50
|
+
{ :title => 'item1', :href => '/item1', :paths => [['/item', 'post']] },
|
51
|
+
{ :title => 'item2', :href => '/item2', :paths => [['/item', 'get']] }
|
52
|
+
]
|
53
|
+
request = mock('request')
|
54
|
+
request.should_receive(:path).once.and_return('/item')
|
55
|
+
request.should_receive(:method).once.and_return('get')
|
56
|
+
request.should_receive(:params).once.and_return({})
|
57
|
+
@menu = Mmmenu.new(:items => items, :request => request )
|
58
|
+
set_menu_markup
|
59
|
+
@menu.build.should == <<END
|
60
|
+
item1: /item1
|
61
|
+
item2: /item2 current
|
62
|
+
END
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
it "chooses the current item with a specific request param" do
|
67
|
+
items = [
|
68
|
+
{ :title => 'item1', :href => '/item1', :paths => [['/item1', 'get', {:param => 1}]] },
|
69
|
+
{ :title => 'item2', :href => '/item1', :paths => [['/item1', 'get', {:param => 2}]] },
|
70
|
+
{ :title => 'item3', :href => '/item1', :paths => [['/item1', 'get', {:param => nil}]] }
|
71
|
+
]
|
72
|
+
request = mock('request')
|
73
|
+
request.should_receive(:path).once.and_return('/item1')
|
74
|
+
request.should_receive(:params).once.and_return({"param" => "1"})
|
75
|
+
request.should_receive(:method).once.and_return('get')
|
76
|
+
@menu = Mmmenu.new(:items => items, :request => request )
|
77
|
+
set_menu_markup
|
78
|
+
@menu.build.should == <<END
|
79
|
+
item1: /item1 current
|
80
|
+
item2: /item1
|
81
|
+
item3: /item1
|
82
|
+
END
|
83
|
+
end
|
84
|
+
|
85
|
+
it "chooses current item when forced to do so by explicitly set current_item property" do
|
86
|
+
items = [
|
87
|
+
{ :title => 'item1', :href => '/item1' },
|
88
|
+
{ :title => 'item2', :href => '/item2' },
|
89
|
+
{ :title => 'item3', :href => '/item3' }
|
90
|
+
]
|
91
|
+
request = mock('request')
|
92
|
+
request.should_receive(:path).once.and_return('/item1')
|
93
|
+
request.should_receive(:method).once.and_return('get')
|
94
|
+
request.should_receive(:params).once
|
95
|
+
@menu = Mmmenu.new(:items => items, :request => request )
|
96
|
+
@menu.current_item = "/item2"
|
97
|
+
set_menu_markup
|
98
|
+
|
99
|
+
@menu.build.should == <<END
|
100
|
+
item1: /item1
|
101
|
+
item2: /item2 current
|
102
|
+
item3: /item3
|
103
|
+
END
|
104
|
+
end
|
105
|
+
|
106
|
+
it "creates menu items in a block using nice DSL" do
|
107
|
+
|
108
|
+
@menu = Mmmenu.new(:request => @request) do |m|
|
109
|
+
m.add 'Item1', '/items1', :match_subpaths => true
|
110
|
+
m.add 'Item2', '/items2' do |subm|
|
111
|
+
subm.add 'New', '/item2/new'
|
112
|
+
subm.add 'Edit', '/item2/edit'
|
113
|
+
end
|
114
|
+
end
|
115
|
+
set_menu_markup
|
116
|
+
|
117
|
+
@menu.build.should == <<END
|
118
|
+
Item1: /items1 current
|
119
|
+
Item2: /items2
|
120
|
+
New: /item2/new
|
121
|
+
Edit: /item2/edit
|
122
|
+
END
|
123
|
+
|
124
|
+
end
|
125
|
+
|
126
|
+
it "creates menu items in a block using nice DSL and additional options" do
|
127
|
+
|
128
|
+
@menu = Mmmenu.new(:request => @request) do |m|
|
129
|
+
m.add 'Item1', '/items1', :match_subpaths => true
|
130
|
+
m.add 'Item2', '/items2' do |subm|
|
131
|
+
subm.add 'New', '/item2/new'
|
132
|
+
subm.add 'Edit', '/item2/edit'
|
133
|
+
end
|
134
|
+
end
|
135
|
+
set_menu_markup
|
136
|
+
|
137
|
+
@menu.build.should == <<END
|
138
|
+
Item1: /items1 current
|
139
|
+
Item2: /items2
|
140
|
+
New: /item2/new
|
141
|
+
Edit: /item2/edit
|
142
|
+
END
|
143
|
+
|
144
|
+
end
|
145
|
+
|
146
|
+
def set_menu_markup(level=1)
|
147
|
+
@menu.item_markup(level) do |link, text, options|
|
148
|
+
"#{text}: #{link}\n"
|
149
|
+
end
|
150
|
+
@menu.current_item_markup(level) do |link, text, options|
|
151
|
+
"#{text}: #{link} current\n"
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
data/spec/spec_helper.rb
ADDED
File without changes
|
metadata
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mmmenu
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.5.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Roman Snitko
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-02-27 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bundler
|
16
|
+
requirement: &24009500 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.0.0
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *24009500
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rspec
|
27
|
+
requirement: &24007940 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ~>
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 2.8.0
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *24007940
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: jeweler
|
38
|
+
requirement: &24006480 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 1.6.4
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *24006480
|
47
|
+
description: Defines multilevel menu structures, uses custom html templates to render
|
48
|
+
them.
|
49
|
+
email: roman.snitko@gmail.com
|
50
|
+
executables: []
|
51
|
+
extensions: []
|
52
|
+
extra_rdoc_files:
|
53
|
+
- README.markdown
|
54
|
+
files:
|
55
|
+
- Gemfile
|
56
|
+
- Gemfile.lock
|
57
|
+
- README.markdown
|
58
|
+
- Rakefile
|
59
|
+
- VERSION
|
60
|
+
- lib/generators/mmmenu/mmmenu_generator.rb
|
61
|
+
- lib/generators/templates/helpers/mmmenu_helper.rb
|
62
|
+
- lib/generators/templates/views/mmmenu/_current_item.erb
|
63
|
+
- lib/generators/templates/views/mmmenu/_item.erb
|
64
|
+
- lib/generators/templates/views/mmmenu/_level_1.erb
|
65
|
+
- lib/generators/templates/views/mmmenu/_level_2.erb
|
66
|
+
- lib/mmmenu.rb
|
67
|
+
- lib/mmmenu/engine.rb
|
68
|
+
- lib/mmmenu/level.rb
|
69
|
+
- lib/mmmenu/version.rb
|
70
|
+
- mmmenu.gemspec
|
71
|
+
- spec/lib/mmmenu_level_spec.rb
|
72
|
+
- spec/lib/mmmenu_spec.rb
|
73
|
+
- spec/spec_helper.rb
|
74
|
+
homepage: http://github.com/snitko/mmmenu
|
75
|
+
licenses:
|
76
|
+
- MIT
|
77
|
+
post_install_message:
|
78
|
+
rdoc_options: []
|
79
|
+
require_paths:
|
80
|
+
- lib
|
81
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
82
|
+
none: false
|
83
|
+
requirements:
|
84
|
+
- - ! '>='
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: '0'
|
87
|
+
segments:
|
88
|
+
- 0
|
89
|
+
hash: 2496057170991287488
|
90
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
|
+
none: false
|
92
|
+
requirements:
|
93
|
+
- - ! '>='
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
96
|
+
requirements: []
|
97
|
+
rubyforge_project:
|
98
|
+
rubygems_version: 1.8.10
|
99
|
+
signing_key:
|
100
|
+
specification_version: 3
|
101
|
+
summary: Flexible menu generator for Rails.
|
102
|
+
test_files: []
|