waiter 0.0.1
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/.gitignore +18 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +134 -0
- data/Rakefile +1 -0
- data/lib/waiter.rb +4 -0
- data/lib/waiter/menu.rb +26 -0
- data/lib/waiter/menu/builder.rb +18 -0
- data/lib/waiter/menu/drawer.rb +91 -0
- data/lib/waiter/version.rb +3 -0
- data/waiter.gemspec +21 -0
- metadata +73 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Daniel Vandersluis
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
# Waiter
|
2
|
+
|
3
|
+
Waiter serves up menus for your application:
|
4
|
+
* Provides a quick DSL for building menus, without having to specify any HTML.
|
5
|
+
* Makes assumptions based on what's provided in the DSL so that the specification does not need to be unnecessarily verbose.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
gem 'waiter'
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install waiter
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
### Building a menu
|
24
|
+
|
25
|
+
To create a new menu:
|
26
|
+
|
27
|
+
Waiter::Menu.serve do |menu|
|
28
|
+
menu.file
|
29
|
+
menu.edit
|
30
|
+
menu.view
|
31
|
+
# ...
|
32
|
+
end
|
33
|
+
|
34
|
+
The above defines a menu with three items. By default, each item name (ie. "file", "edit", etc.) corresponds both to the I18n string to use (so `menu.file` will call `I18n.t(:file)`) as well as to the controller to use (`menu.file` will correspond to `FileController`, and the menu item will link to `FileController#index` by default).
|
35
|
+
|
36
|
+
Of course, there will be situations where the path may need to be explicitly specified. In this case, the `:controller`
|
37
|
+
and `:action` options can be specified:
|
38
|
+
|
39
|
+
Waiter::Menu.serve do |menu|
|
40
|
+
menu.file :controller => 'documents', :action => 'show'
|
41
|
+
end
|
42
|
+
|
43
|
+
In this case the File menu will correspond to `DocumentsController#show`, and will only light up for that action. If
|
44
|
+
no specific action is given, the menu item will be lit up for all actions within the given controller.
|
45
|
+
|
46
|
+
There may also be times where you need multiple controllers to light up the same menu item. This can be achieved by
|
47
|
+
passing an array to the `:controllers` option:
|
48
|
+
|
49
|
+
Waiter::Menu.serve do |menu|
|
50
|
+
menu.file :controller => 'documents', :controllers => ['files', 'print', ... ]
|
51
|
+
end
|
52
|
+
|
53
|
+
This will create a menu that links to `DocumentsController#index`, which will be lit up the user hits any action within the `FilesController`, `PrintController`, as well as the `DocumentsController`.
|
54
|
+
|
55
|
+
|
56
|
+
### Submenus
|
57
|
+
|
58
|
+
Menus can also have submenus (currently only one level of nesting is supported when drawing a menu, but infinite
|
59
|
+
levels are supported when building a menu). A submenu is defined with the same syntax as a root menu item, and is
|
60
|
+
passed as a block to the root menu item.
|
61
|
+
|
62
|
+
Submenus automatically assume the controller to use is the controller specified by its root menu item, so a
|
63
|
+
controller does not need to be specified unless it differs. As well, the menu item name is assumed to be used
|
64
|
+
for the action within that controller, so action does not need to be specified unless it differs from the name.
|
65
|
+
An id can also be specified if necessary (with the `:id` option).
|
66
|
+
|
67
|
+
Waiter::Menu.serve do |menu|
|
68
|
+
menu.file, :controllers => ['print'] do |file|
|
69
|
+
file.new # Corresponds to FileController#new
|
70
|
+
file.open :action => :read # Corresponds to FileController#read
|
71
|
+
file.save :action => :write # Corresponds to FileController#write
|
72
|
+
file.print, :controller => 'print' # Corresponds to PrintController#print
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
If an action isn't explicitly given, and the menu item name does not correspond to an actual action, the default
|
77
|
+
action for the controller will be used instead.
|
78
|
+
|
79
|
+
|
80
|
+
### Menu Options
|
81
|
+
|
82
|
+
There are two options that you can use when building your menu. Options can be passed into build when defining
|
83
|
+
a menu or specified once the menu is defined by accessing the menu.options hash.
|
84
|
+
|
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
|
+
`selected`: Allows the selected menu item to be overridden, so a specified menu item can be lit up regardless of
|
94
|
+
what the current controller/action is. This can be useful if there is an action that falls under different menu
|
95
|
+
items depending on application context, or if there is a controller that may correspond to multiple menu items.
|
96
|
+
A good way to achieve this would be to add a before_filter to set an instance variable which is then passed into
|
97
|
+
the selected option.
|
98
|
+
|
99
|
+
before_filter :set_current_menu
|
100
|
+
|
101
|
+
def set_current_menu
|
102
|
+
case params[:action]
|
103
|
+
when 'foo' then @current_menu = "menu1"
|
104
|
+
when 'bar' then @current_menu = "menu2"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
Waiter::Menu.serve(:selected => @current_menu) do |menu|
|
109
|
+
end
|
110
|
+
|
111
|
+
Note that the value passed to `:selected` should match the name of the menu item.
|
112
|
+
|
113
|
+
|
114
|
+
### Drawing (outputting) a menu
|
115
|
+
|
116
|
+
To draw a created menu, use the `Waiter::Menu::Drawer` class. The class requires the current context to be
|
117
|
+
passed in so that `ActionController` methods can be used. In general (ie. if the menu is being drawn from a helper)
|
118
|
+
the context is self.
|
119
|
+
|
120
|
+
menu = Waiter::Menu.serve do |menu|
|
121
|
+
# ...
|
122
|
+
end
|
123
|
+
|
124
|
+
Waiter::Menu::Drawer.new(menu, context).draw
|
125
|
+
|
126
|
+
If a different `Drawer` is desired, `Waiter::Menu::Drawer` can be subclassed and the methods `draw` and `draw_submenu` overridden to provide an alternate format.
|
127
|
+
|
128
|
+
## Contributing
|
129
|
+
|
130
|
+
1. Fork it
|
131
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
132
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
133
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
134
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/lib/waiter.rb
ADDED
data/lib/waiter/menu.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
module Waiter
|
2
|
+
class Menu
|
3
|
+
attr_accessor :options, :submenus
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def serve(options = {})
|
7
|
+
yield Builder.new(obj = self.new(options))
|
8
|
+
obj
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(options = {})
|
13
|
+
@menu_items = []
|
14
|
+
@submenus = {}
|
15
|
+
@options = options
|
16
|
+
end
|
17
|
+
|
18
|
+
def add(name, args = {})
|
19
|
+
@menu_items << [name, args]
|
20
|
+
end
|
21
|
+
|
22
|
+
def items
|
23
|
+
@menu_items
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Waiter
|
2
|
+
class Menu
|
3
|
+
# Builds a menu structure from the DSL
|
4
|
+
class Builder
|
5
|
+
def initialize(menu)
|
6
|
+
@menu = menu
|
7
|
+
end
|
8
|
+
|
9
|
+
def method_missing(name, *args, &block)
|
10
|
+
@menu.add name, *args
|
11
|
+
if block_given?
|
12
|
+
@menu.submenus[name] = Menu.new(@menu.options.merge(:controller => (args.first.andand[:controller] || name)))
|
13
|
+
yield Builder.new(@menu.submenus[name])
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
module Waiter
|
2
|
+
class Menu
|
3
|
+
# Outputs a Menu into HTML
|
4
|
+
class Drawer
|
5
|
+
# @context is a Controller binding, which allows the ActionView helpers to work in the correct context
|
6
|
+
delegate :link_to, :content_tag, :params, :to => :@context
|
7
|
+
|
8
|
+
def initialize(menu, context)
|
9
|
+
@context = context
|
10
|
+
@menu = menu
|
11
|
+
end
|
12
|
+
|
13
|
+
def draw
|
14
|
+
content_tag :div, :id => "menu" do
|
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
|
61
|
+
end
|
62
|
+
|
63
|
+
protected
|
64
|
+
def menu_selected?(name, controller, action = nil, controllers = [])
|
65
|
+
controllers = [] unless controllers
|
66
|
+
|
67
|
+
if @menu.options[:selected]
|
68
|
+
name.to_s == @menu.options[:selected].to_s
|
69
|
+
else
|
70
|
+
return true if controllers.map(&:to_s).include?(params[:controller])
|
71
|
+
|
72
|
+
selected = params[:controller] == controller.to_s
|
73
|
+
|
74
|
+
if action.is_a? Array
|
75
|
+
selected &&= action.map(&:to_s).include?(params[:action])
|
76
|
+
elsif action
|
77
|
+
selected &&= params[:action] == action.to_s
|
78
|
+
end
|
79
|
+
|
80
|
+
selected
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
class String
|
88
|
+
def absolutify
|
89
|
+
self.gsub(%r{^(?!/)}, "/")
|
90
|
+
end
|
91
|
+
end
|
data/waiter.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'waiter/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "waiter"
|
8
|
+
gem.version = Waiter::VERSION
|
9
|
+
gem.authors = ["Daniel Vandersluis"]
|
10
|
+
gem.email = ["dvandersluis@selfmgmt.com"]
|
11
|
+
gem.description = "Provides an easy DSL for serving up menus"
|
12
|
+
gem.summary = "Quick and easy DSL for generating menus for use in Rails applications"
|
13
|
+
gem.homepage = "https://github.com/dvandersluis/waiter"
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
|
20
|
+
gem.add_dependency "activesupport", "> 3.0.0"
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: waiter
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.0.1
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Daniel Vandersluis
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-11-23 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
prerelease: false
|
16
|
+
type: :runtime
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ! '>'
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 3.0.0
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ! '>'
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
version: 3.0.0
|
29
|
+
name: activesupport
|
30
|
+
description: Provides an easy DSL for serving up menus
|
31
|
+
email:
|
32
|
+
- dvandersluis@selfmgmt.com
|
33
|
+
executables: []
|
34
|
+
extensions: []
|
35
|
+
extra_rdoc_files: []
|
36
|
+
files:
|
37
|
+
- .gitignore
|
38
|
+
- Gemfile
|
39
|
+
- LICENSE.txt
|
40
|
+
- README.md
|
41
|
+
- Rakefile
|
42
|
+
- lib/waiter.rb
|
43
|
+
- lib/waiter/menu.rb
|
44
|
+
- lib/waiter/menu/builder.rb
|
45
|
+
- lib/waiter/menu/drawer.rb
|
46
|
+
- lib/waiter/version.rb
|
47
|
+
- waiter.gemspec
|
48
|
+
homepage: https://github.com/dvandersluis/waiter
|
49
|
+
licenses: []
|
50
|
+
post_install_message:
|
51
|
+
rdoc_options: []
|
52
|
+
require_paths:
|
53
|
+
- lib
|
54
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ! '>='
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '0'
|
60
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
requirements: []
|
67
|
+
rubyforge_project:
|
68
|
+
rubygems_version: 1.8.24
|
69
|
+
signing_key:
|
70
|
+
specification_version: 3
|
71
|
+
summary: Quick and easy DSL for generating menus for use in Rails applications
|
72
|
+
test_files: []
|
73
|
+
has_rdoc:
|