waiter 0.0.1 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.rspec +2 -0
- data/README.md +34 -32
- data/Rakefile +10 -0
- data/app/assets/images/waiter/bg.gif +0 -0
- data/app/assets/images/waiter/selected-bg.gif +0 -0
- data/app/assets/stylesheets/waiter/menu.css.scss +129 -0
- data/app/views/waiter/_item.haml +8 -0
- data/app/views/waiter/_menu_bar.haml +3 -0
- data/app/views/waiter/_section.haml +5 -0
- data/app/views/waiter/_sub_item.haml +2 -0
- data/app/views/waiter/_submenu.haml +6 -0
- data/lib/waiter/dsl.rb +24 -0
- data/lib/waiter/menu/drawer.rb +7 -55
- data/lib/waiter/menu/item.rb +137 -0
- data/lib/waiter/menu/item_list.rb +19 -0
- data/lib/waiter/menu/section.rb +21 -0
- data/lib/waiter/menu.rb +54 -12
- data/lib/waiter/version.rb +1 -1
- data/lib/waiter.rb +12 -3
- data/spec/spec_helper.rb +89 -0
- data/spec/support/matchers/have_a_submenu.rb +34 -0
- data/spec/support/matchers/have_exactly.rb +24 -0
- data/spec/support/matchers/have_menu_items_named.rb +32 -0
- data/spec/support/matchers/have_options.rb +23 -0
- data/spec/support/shared_examples/menu_item_shared_examples.rb +67 -0
- data/spec/waiter/menu/item_list_spec.rb +38 -0
- data/spec/waiter/menu/item_spec.rb +161 -0
- data/spec/waiter/menu/section_spec.rb +21 -0
- data/spec/waiter/menu_spec.rb +298 -0
- data/waiter.gemspec +4 -0
- metadata +97 -13
- data/lib/waiter/menu/builder.rb +0 -18
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
YmQ1YzdlZmU3MDM4ZDgwOWQ0ZmNhMjI5MDcwNGYxZTUwMTE5YzI1MQ==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
ZTg1ZmIzMDc1MmQ4MDQ0MDk1ZDRmZjIwNzViMDVmYmFlYzBjYzhiNQ==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
NTBhMTMxOWQ4ZmNiZWEwZmNkMDkwMDBiZDA3YjIyODNhYTM1M2ZhNDgwYjY2
|
10
|
+
OThhODZmYmQ3MDVhMWQzYTY4YWM4NWMwNGFiNzg5MTk4NWM1OTlkODIxMDM3
|
11
|
+
ZTAwYjFhMTE1YWU3ZDhjYzE5ZDkyODI1ZjllNjdmMDEyZTY2ZGQ=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
MGVjZTZmMWY4NmY3MTM4NDI0NGQyNjBkMTc1MTFkZGM4OWFkYjU1YTc2MmIz
|
14
|
+
MWNkNWYwNjhiMWY5YTRhNGQ2NWNkMzdjMDQzZDA3YzBkN2FlNzEwMzc4OTIz
|
15
|
+
NzU4YmZjZTA2YjFhNDVmNWEwMTM2NTJhMzk1ZGIyOTZkMzVjNGY=
|
data/.rspec
ADDED
data/README.md
CHANGED
@@ -24,33 +24,41 @@ Or install it yourself as:
|
|
24
24
|
|
25
25
|
To create a new menu:
|
26
26
|
|
27
|
-
Waiter::Menu.serve do
|
28
|
-
|
29
|
-
|
30
|
-
|
27
|
+
Waiter::Menu.serve(:my_menu) do
|
28
|
+
file
|
29
|
+
edit
|
30
|
+
view
|
31
31
|
# ...
|
32
32
|
end
|
33
33
|
|
34
|
-
The above defines a menu with three items. By default, each item name (ie. "file", "edit", etc.) corresponds both to the
|
34
|
+
The above defines a menu with three items. By default, each item name (ie. "file", "edit", etc.) corresponds both to the
|
35
|
+
I18n string to use (so `file` will call `I18n.t(:file, scope: [:menu, :my_menu])`) as well as to the controller to use
|
36
|
+
(`file` will correspond to `FileController`, and the menu item will link to `FileController#index` by default).
|
35
37
|
|
36
|
-
Of course, there will be situations where the path may need to be explicitly specified. In this case,
|
37
|
-
and `:action` options can be specified:
|
38
|
+
Of course, there will be situations where the path may need to be explicitly specified. In this case, you can use a path finder:
|
38
39
|
|
39
|
-
Waiter::Menu
|
40
|
-
menu.file
|
40
|
+
Waiter::Menu::serve do
|
41
|
+
menu.file documents_path
|
41
42
|
end
|
43
|
+
|
44
|
+
Alternately, the `:controller` and `:action` options can be specified:
|
42
45
|
|
43
|
-
|
44
|
-
|
46
|
+
Waiter::Menu.serve do
|
47
|
+
menu.file :controller => :documents, :action => :index
|
48
|
+
end
|
49
|
+
|
50
|
+
In both of the above examples, the File menu will correspond to `DocumentsController#index`, and will be shown as selected
|
51
|
+
for any action within that controller.
|
45
52
|
|
46
53
|
There may also be times where you need multiple controllers to light up the same menu item. This can be achieved by
|
47
54
|
passing an array to the `:controllers` option:
|
48
55
|
|
49
|
-
Waiter::Menu.serve do
|
50
|
-
|
56
|
+
Waiter::Menu.serve do
|
57
|
+
file :controllers => ['files', 'print', ... ]
|
51
58
|
end
|
52
59
|
|
53
|
-
This will create a menu that links to `DocumentsController#index`, which will be lit up the user hits any action within
|
60
|
+
This will create a menu that links to `DocumentsController#index`, which will be lit up the user hits any action within
|
61
|
+
the `FilesController`, `PrintController`, as well as the `DocumentsController`.
|
54
62
|
|
55
63
|
|
56
64
|
### Submenus
|
@@ -61,20 +69,18 @@ passed as a block to the root menu item.
|
|
61
69
|
|
62
70
|
Submenus automatically assume the controller to use is the controller specified by its root menu item, so a
|
63
71
|
controller does not need to be specified unless it differs. As well, the menu item name is assumed to be used
|
64
|
-
for the
|
72
|
+
for the controller for that item, so it does not need to be specified unless it differs from the name.
|
65
73
|
An id can also be specified if necessary (with the `:id` option).
|
66
74
|
|
67
|
-
Waiter::Menu.serve do
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
file.print, :controller => 'print' # Corresponds to PrintController#print
|
75
|
+
Waiter::Menu.serve do
|
76
|
+
file do
|
77
|
+
new # Corresponds to FileController#new
|
78
|
+
print print_file_path # Corresponds to PrintController#print
|
79
|
+
list :action => :all # Corresponds to ListController#all
|
73
80
|
end
|
74
81
|
end
|
75
82
|
|
76
|
-
If
|
77
|
-
action for the controller will be used instead.
|
83
|
+
If a path hash is given without an `action` key, the index action will be inferred.
|
78
84
|
|
79
85
|
|
80
86
|
### Menu Options
|
@@ -82,14 +88,6 @@ action for the controller will be used instead.
|
|
82
88
|
There are two options that you can use when building your menu. Options can be passed into build when defining
|
83
89
|
a menu or specified once the menu is defined by accessing the menu.options hash.
|
84
90
|
|
85
|
-
`string_prefix`: Allows for a prefix for I18n strings to be specified.
|
86
|
-
|
87
|
-
Waiter::Menu.serve(:string_prefix => 'menu_') do |menu|
|
88
|
-
menu.file
|
89
|
-
end
|
90
|
-
|
91
|
-
# will use I18n.t(:menu_file) instead of I18n.t(:file)
|
92
|
-
|
93
91
|
`selected`: Allows the selected menu item to be overridden, so a specified menu item can be lit up regardless of
|
94
92
|
what the current controller/action is. This can be useful if there is an action that falls under different menu
|
95
93
|
items depending on application context, or if there is a controller that may correspond to multiple menu items.
|
@@ -123,7 +121,11 @@ the context is self.
|
|
123
121
|
|
124
122
|
Waiter::Menu::Drawer.new(menu, context).draw
|
125
123
|
|
126
|
-
If a different `Drawer` is desired, `Waiter::Menu::Drawer` can be subclassed and the
|
124
|
+
If a different `Drawer` is desired, `Waiter::Menu::Drawer` can be subclassed and the method `draw` overridden to provide an alternate format.
|
125
|
+
|
126
|
+
Alternately, `Waiter::Menu` responds to `draw` directly:
|
127
|
+
|
128
|
+
menu.draw(context)
|
127
129
|
|
128
130
|
## Contributing
|
129
131
|
|
data/Rakefile
CHANGED
Binary file
|
Binary file
|
@@ -0,0 +1,129 @@
|
|
1
|
+
/*
|
2
|
+
* Menu bar
|
3
|
+
*/
|
4
|
+
.waiter-menu
|
5
|
+
{
|
6
|
+
width: 100%;
|
7
|
+
display: block;
|
8
|
+
height: 40px;
|
9
|
+
border: 0px;
|
10
|
+
background: image-url('waiter/bg.gif') repeat-x left top;
|
11
|
+
|
12
|
+
div.top-item
|
13
|
+
{
|
14
|
+
float: left;
|
15
|
+
position: relative;
|
16
|
+
top: 10px;
|
17
|
+
height: 20px;
|
18
|
+
min-width: 80px;
|
19
|
+
border-right: 1px solid #e6e9ef;
|
20
|
+
border-left: 0;
|
21
|
+
margin: 0;
|
22
|
+
text-align: center;
|
23
|
+
/* Fix IE7 z-index bug - See http://brenelz.com/blog/squish-the-internet-explorer-z-index-bug/ */
|
24
|
+
z-index: 10501;
|
25
|
+
color: #e6e9ef;
|
26
|
+
font-family: "Arial";
|
27
|
+
font-weight: bold;
|
28
|
+
font-size: 14px;
|
29
|
+
padding: 5px 15px 5px 10px;
|
30
|
+
cursor: default;
|
31
|
+
|
32
|
+
a
|
33
|
+
{
|
34
|
+
color: #FFF;
|
35
|
+
display: block;
|
36
|
+
height: 20px;
|
37
|
+
min-width: 80px;
|
38
|
+
text-decoration: none;
|
39
|
+
}
|
40
|
+
|
41
|
+
&:hover, &.selected
|
42
|
+
{
|
43
|
+
color: #FFF;
|
44
|
+
background: image-url('waiter/selected-bg.gif') repeat-x left top;
|
45
|
+
}
|
46
|
+
|
47
|
+
$submenu-border-color: #888;
|
48
|
+
$submenu-item-border-color: #CCC;
|
49
|
+
$submenu-item-height: 20px;
|
50
|
+
|
51
|
+
ul.submenu
|
52
|
+
{
|
53
|
+
position: absolute;
|
54
|
+
top: 30px;
|
55
|
+
left: 0;
|
56
|
+
background-color: #ffffff;
|
57
|
+
display: none;
|
58
|
+
z-index: 10500;
|
59
|
+
margin: 0;
|
60
|
+
padding: 0;
|
61
|
+
border: 2px solid $submenu-border-color;
|
62
|
+
|
63
|
+
li
|
64
|
+
{
|
65
|
+
list-style-type: none;
|
66
|
+
min-height: $submenu-item-height;
|
67
|
+
line-height: $submenu-item-height;
|
68
|
+
vertical-align: middle;
|
69
|
+
background-color: #e6e9ef;
|
70
|
+
border-top: 1px solid $submenu-item-border-color;
|
71
|
+
min-width: 150px;
|
72
|
+
text-align: left;
|
73
|
+
white-space: nowrap;
|
74
|
+
color: #000000;
|
75
|
+
border-collapse: collapse;
|
76
|
+
|
77
|
+
font-family: "Arial";
|
78
|
+
font-weight: normal;
|
79
|
+
font-size: 12px;
|
80
|
+
white-space: nowrap;
|
81
|
+
|
82
|
+
a, a:hover
|
83
|
+
{
|
84
|
+
color: #000000;
|
85
|
+
font-weight: normal;
|
86
|
+
padding-left: 8px;
|
87
|
+
padding-right: 8px;
|
88
|
+
line-height: $submenu-item-height + 12px;
|
89
|
+
height: $submenu-item-height + 12px;
|
90
|
+
}
|
91
|
+
|
92
|
+
&:first-child
|
93
|
+
{
|
94
|
+
border-top: 0;
|
95
|
+
}
|
96
|
+
|
97
|
+
&:hover
|
98
|
+
{
|
99
|
+
background-color: #ff9900;
|
100
|
+
}
|
101
|
+
|
102
|
+
&.separator
|
103
|
+
{
|
104
|
+
min-height: 0;
|
105
|
+
max-height: 0;
|
106
|
+
padding: 0;
|
107
|
+
border: 0;
|
108
|
+
}
|
109
|
+
}
|
110
|
+
|
111
|
+
li.separator + li
|
112
|
+
{
|
113
|
+
border-top: 2px solid $submenu-border-color;
|
114
|
+
}
|
115
|
+
|
116
|
+
li.separator + li.separator
|
117
|
+
{
|
118
|
+
display: none;
|
119
|
+
}
|
120
|
+
}
|
121
|
+
|
122
|
+
&:hover ul.submenu
|
123
|
+
{
|
124
|
+
background-color: #333333;
|
125
|
+
/*border:1px solid #333333;*/
|
126
|
+
display: block;
|
127
|
+
}
|
128
|
+
}
|
129
|
+
}
|
data/lib/waiter/dsl.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'active_support/core_ext/array/extract_options'
|
2
|
+
|
3
|
+
module Waiter
|
4
|
+
module DSL
|
5
|
+
def section(options = {}, &block)
|
6
|
+
add_section(options, &block)
|
7
|
+
end
|
8
|
+
|
9
|
+
def method_missing(name, *args, &block)
|
10
|
+
return context.send(name, *args, &block) if context.respond_to?(name)
|
11
|
+
|
12
|
+
path = args.shift
|
13
|
+
options = args.extract_options!
|
14
|
+
|
15
|
+
if path.is_a?(Hash) && !(path.key?(:controller) || path.key?(:action))
|
16
|
+
options, path = path, nil
|
17
|
+
end
|
18
|
+
|
19
|
+
options[:controllers] ||= []
|
20
|
+
add(name, path, options, &block)
|
21
|
+
return nil
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/waiter/menu/drawer.rb
CHANGED
@@ -1,63 +1,21 @@
|
|
1
|
+
require 'active_support/core_ext/module/delegation'
|
2
|
+
|
1
3
|
module Waiter
|
2
4
|
class Menu
|
3
5
|
# Outputs a Menu into HTML
|
4
6
|
class Drawer
|
5
|
-
|
6
|
-
|
7
|
+
attr_reader :menu, :context
|
8
|
+
|
9
|
+
delegate :render, :link_to, :content_tag, :params, :to => :context
|
7
10
|
|
8
11
|
def initialize(menu, context)
|
12
|
+
# context is a Controller binding, which allows the ActionView helpers to work in the correct context
|
9
13
|
@context = context
|
10
14
|
@menu = menu
|
11
15
|
end
|
12
16
|
|
13
17
|
def draw
|
14
|
-
|
15
|
-
out = ActiveSupport::SafeBuffer.new
|
16
|
-
|
17
|
-
@menu.items.each do |(name, menu)|
|
18
|
-
menu[:controller] = name unless menu[:controller]
|
19
|
-
|
20
|
-
# Allow an :if option which will only display the menu if true
|
21
|
-
if !menu[:if] or menu[:if].call
|
22
|
-
# If there is a :selected option, use that instead of trying to determine the selected menu item
|
23
|
-
selected = menu_selected?(name, menu[:controller], menu[:action] || menu[:actions], menu.delete(:controllers))
|
24
|
-
|
25
|
-
out << content_tag(:span, :class => selected ? "selected" : nil) do
|
26
|
-
out2 = ActiveSupport::SafeBuffer.new
|
27
|
-
out2 << link_to(I18n.t(@menu.options[:string_prefix].to_s + name.to_s), { :controller => menu.delete(:controller).to_s.absolutify, :action => (menu.delete(:action) || menu.delete(:actions).andand.first) }.merge(menu))
|
28
|
-
out2 << draw_submenu(@menu.submenus[name]) if @menu.submenus[name]
|
29
|
-
out2
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
out << '<br class="clear" />'.html_safe
|
35
|
-
out
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
def draw_submenu(submenu)
|
40
|
-
content_tag :table, :class => "dropdown", :cellspacing => 0 do
|
41
|
-
|
42
|
-
out = ActiveSupport::SafeBuffer.new
|
43
|
-
|
44
|
-
submenu.items.each do |(name, menu)|
|
45
|
-
out << content_tag(:tr) do
|
46
|
-
content_tag :td do
|
47
|
-
controller = menu.delete(:controller) || submenu.options[:controller]
|
48
|
-
action =
|
49
|
-
menu.delete(:action) || begin
|
50
|
-
controller_class = "#{controller}_controller".classify.constantize
|
51
|
-
controller_class.action_methods.include?(name.to_s) ? name : nil
|
52
|
-
end rescue nil || :index
|
53
|
-
|
54
|
-
link_to I18n.t(submenu.options[:string_prefix].to_s + name.to_s), { :controller => controller.to_s.absolutify, :action => action }.merge(menu)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
out
|
60
|
-
end
|
18
|
+
render partial: 'waiter/menu_bar', locals: { menu: menu }
|
61
19
|
end
|
62
20
|
|
63
21
|
protected
|
@@ -82,10 +40,4 @@ module Waiter
|
|
82
40
|
end
|
83
41
|
end
|
84
42
|
end
|
85
|
-
end
|
86
|
-
|
87
|
-
class String
|
88
|
-
def absolutify
|
89
|
-
self.gsub(%r{^(?!/)}, "/")
|
90
|
-
end
|
91
43
|
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
require 'active_support/core_ext/object/try'
|
2
|
+
require 'active_support/core_ext/hash/reverse_merge'
|
3
|
+
require 'active_support/core_ext/module/delegation'
|
4
|
+
require 'active_support/core_ext/object/blank'
|
5
|
+
|
6
|
+
module Waiter
|
7
|
+
class Menu
|
8
|
+
class Item
|
9
|
+
attr_reader :parent, :name, :path, :submenu
|
10
|
+
attr_accessor :options
|
11
|
+
delegate :context, to: :parent
|
12
|
+
|
13
|
+
def initialize(parent, name, path = {}, options = {}, &block)
|
14
|
+
@parent = parent
|
15
|
+
@name = name
|
16
|
+
@path = complete_path_for(path)
|
17
|
+
@options = options
|
18
|
+
|
19
|
+
collect_controllers
|
20
|
+
|
21
|
+
@options.reverse_merge!(parent.options || {})
|
22
|
+
|
23
|
+
if block_given?
|
24
|
+
@submenu = Menu.new([@parent.name, name].compact, @parent.context, @options.dup)
|
25
|
+
@submenu.parent = self
|
26
|
+
@submenu.update(&block)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def section?
|
31
|
+
false
|
32
|
+
end
|
33
|
+
|
34
|
+
def items
|
35
|
+
@submenu.try(:items) || []
|
36
|
+
end
|
37
|
+
|
38
|
+
def [](name)
|
39
|
+
return nil if items.empty?
|
40
|
+
items[name]
|
41
|
+
end
|
42
|
+
|
43
|
+
def submenu?
|
44
|
+
submenu.present?
|
45
|
+
end
|
46
|
+
|
47
|
+
def menu_title
|
48
|
+
translate(:title)
|
49
|
+
end
|
50
|
+
|
51
|
+
def item_title
|
52
|
+
translate(name)
|
53
|
+
end
|
54
|
+
|
55
|
+
def controller
|
56
|
+
@controller ||= find_controller
|
57
|
+
end
|
58
|
+
|
59
|
+
def controllers
|
60
|
+
options[:controllers]
|
61
|
+
end
|
62
|
+
|
63
|
+
def selected?
|
64
|
+
return name == options[:selected] if options[:selected]
|
65
|
+
|
66
|
+
current_controller = context.params[:controller]
|
67
|
+
|
68
|
+
if wildcard_controllers.any?
|
69
|
+
return true if wildcard_controllers.any? do |c|
|
70
|
+
r = Regexp.new(c.sub(%r{/\*$}, '(/*)?').gsub('*', '.*'))
|
71
|
+
r =~ current_controller
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
controllers.include?(current_controller)
|
76
|
+
end
|
77
|
+
|
78
|
+
def inspect
|
79
|
+
"#<#{self.class.name}:#{'0x%x' % (36971870 << 1)} name=#{name.inspect} options=#{options.inspect} parent=#{parent.inspect} submenu=#{submenu.inspect}>"
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def translate(key)
|
85
|
+
scope = i18n_scope
|
86
|
+
scope.pop if scope.last == key
|
87
|
+
I18n.t(key, scope: scope, cascade: true)
|
88
|
+
end
|
89
|
+
|
90
|
+
def i18n_scope
|
91
|
+
[:menu, parent.name, name].flatten.compact
|
92
|
+
end
|
93
|
+
|
94
|
+
def complete_path_for(path)
|
95
|
+
return if path.nil? && parent.top?
|
96
|
+
|
97
|
+
case path
|
98
|
+
when String
|
99
|
+
path
|
100
|
+
|
101
|
+
when Hash, NilClass
|
102
|
+
path ||= {}
|
103
|
+
path[:action] ||= :index
|
104
|
+
path[:controller] ||= name
|
105
|
+
path[:controller] = path[:controller].to_s.gsub(%r{^(?!/)}, '/')
|
106
|
+
path
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def find_controller
|
111
|
+
case path
|
112
|
+
when Hash
|
113
|
+
path[:controller].to_s.gsub(%r{^/}, '')
|
114
|
+
|
115
|
+
when String
|
116
|
+
request = parent.context.request
|
117
|
+
route = Rails.application.routes.recognize_path("#{request.protocol}#{request.host}#{path}")
|
118
|
+
route ? route[:controller].to_s : nil
|
119
|
+
end
|
120
|
+
|
121
|
+
rescue ActionController::RoutingError
|
122
|
+
nil
|
123
|
+
end
|
124
|
+
|
125
|
+
def collect_controllers
|
126
|
+
target = parent.top? ? self : parent
|
127
|
+
target.options[:controllers] ||= []
|
128
|
+
target.options[:controllers] << controller unless controller.blank?
|
129
|
+
target.options[:controllers].uniq!
|
130
|
+
end
|
131
|
+
|
132
|
+
def wildcard_controllers
|
133
|
+
@wildcard_controllers ||= controllers.select{ |c| c['*'] }
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Waiter
|
2
|
+
class Menu
|
3
|
+
class ItemList < Array
|
4
|
+
def include?(other)
|
5
|
+
return map(&:name).include?(other) if other.is_a? Symbol
|
6
|
+
super
|
7
|
+
end
|
8
|
+
|
9
|
+
def [](index)
|
10
|
+
return detect{ |item| item.name == index } if index.is_a? Symbol
|
11
|
+
super
|
12
|
+
end
|
13
|
+
|
14
|
+
def names
|
15
|
+
map(&:name)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'waiter/menu/item'
|
2
|
+
|
3
|
+
module Waiter
|
4
|
+
class Menu
|
5
|
+
class Section < Item
|
6
|
+
def initialize(parent, options = {}, &block)
|
7
|
+
super(parent, nil, nil, options, &block)
|
8
|
+
end
|
9
|
+
|
10
|
+
def section?
|
11
|
+
true
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def complete_path_for(*)
|
17
|
+
nil
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/waiter/menu.rb
CHANGED
@@ -1,26 +1,68 @@
|
|
1
|
+
require 'waiter/dsl'
|
2
|
+
require 'waiter/menu/item_list'
|
3
|
+
require 'waiter/menu/item'
|
4
|
+
require 'waiter/menu/section'
|
5
|
+
require 'waiter/menu/drawer'
|
6
|
+
|
1
7
|
module Waiter
|
2
8
|
class Menu
|
3
|
-
attr_accessor :options, :
|
9
|
+
attr_accessor :name, :context, :options, :submenu, :parent
|
10
|
+
|
11
|
+
include DSL
|
4
12
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
obj
|
13
|
+
def self.serve(name, context = nil, options = {}, &block)
|
14
|
+
new(name, context, options).tap do |menu|
|
15
|
+
menu.update(&block)
|
9
16
|
end
|
10
17
|
end
|
11
18
|
|
12
|
-
def initialize(options = {})
|
13
|
-
@
|
14
|
-
@
|
19
|
+
def initialize(name, context, options = {})
|
20
|
+
@name = name
|
21
|
+
@context = context
|
22
|
+
@items = ItemList.new
|
15
23
|
@options = options
|
16
24
|
end
|
17
25
|
|
18
|
-
def
|
19
|
-
|
26
|
+
def update(&block)
|
27
|
+
instance_eval(&block)
|
28
|
+
end
|
29
|
+
|
30
|
+
def draw(context = nil)
|
31
|
+
Drawer.new(self, context).draw
|
32
|
+
end
|
33
|
+
|
34
|
+
def add(name, path = {}, options = {}, &block)
|
35
|
+
items << Item.new(self, name, path, options, &block)
|
36
|
+
end
|
37
|
+
|
38
|
+
def add_section(options = {}, &block)
|
39
|
+
section = Section.new(self, options, &block)
|
40
|
+
items << section if section.items.any?
|
41
|
+
end
|
42
|
+
|
43
|
+
def sections
|
44
|
+
items.select(&:section?) || []
|
45
|
+
end
|
46
|
+
|
47
|
+
def [](name)
|
48
|
+
items[name]
|
49
|
+
end
|
50
|
+
|
51
|
+
def items(sorted = false)
|
52
|
+
return @items unless sorted
|
53
|
+
|
54
|
+
items = @items.dup
|
55
|
+
items.sort_by!(&options[:sort].to_sym) if options.fetch(:sort, false)
|
56
|
+
items.reverse! if options.fetch(:reverse, false)
|
57
|
+
ItemList.new(items)
|
58
|
+
end
|
59
|
+
|
60
|
+
def top?
|
61
|
+
parent.nil?
|
20
62
|
end
|
21
63
|
|
22
|
-
def
|
23
|
-
|
64
|
+
def inspect
|
65
|
+
"#<#{self.class.name}:#{'0x%x' % (36971870 << 1)} name=#{name.inspect} options=#{options.inspect}>"
|
24
66
|
end
|
25
67
|
end
|
26
68
|
end
|
data/lib/waiter/version.rb
CHANGED