mmmenu 0.5.0
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.
- 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: []
|