waiter 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|