waiter 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .idea
6
+ .yardoc
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in menu_builder.gemspec
4
+ gemspec
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
@@ -0,0 +1,4 @@
1
+ require "waiter/version"
2
+ require "waiter/menu"
3
+ require "waiter/menu/builder"
4
+ require "waiter/menu/drawer"
@@ -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
@@ -0,0 +1,3 @@
1
+ module Waiter
2
+ VERSION = "0.0.1"
3
+ 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: