waiter 0.0.1 → 2.0.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.
- 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