Skip to content
Browse files

Initial commit

  • Loading branch information...
0 parents commit fad63388d711afec1d642a9874ca3ad57f64b228 @mtrudel committed Mar 15, 2010
Showing with 212 additions and 0 deletions.
  1. +43 −0 README
  2. +42 −0 Rakefile
  3. +1 −0 VERSION
  4. +86 −0 lib/base.rb
  5. +35 −0 menutree.gemspec
  6. +5 −0 menutree.rb
43 README
@@ -0,0 +1,43 @@
+Menutree is a framework for presenting a recursive menu shell on the command
+line, inspired by the CLI found in Cisco IOS and other similar products.
+Menutree lets users issue commands via an interactive nested shell interface,
+and also directly from the command line.
+
+The menu structure and commands therein are defined by a directory of ruby
+scripts conforming to a simple interface. Inline help, command completion, and
+readline support are all built in.
+
+Example:
+
+Given a hypothetical program 'ticker' that uses the Menutree gem to manage a list of
+tickets, a sample interaction might look like:
+
+$ ticker
+> init
+Ticker created an empty ticket database
+> add "Do a little dance"
+Ticket "Do a little dance" added as ticket 213
+> rm
+rm> 213
+Ticket 213 ("Do a little dance") removed
+rm> ..
+> exit
+
+Commands could also be run from the shell:
+
+$ ticker add "Make a little love"
+Ticket "Make a little love" added as ticket 214
+
+To implement this, ticker would provide Menutree with a directory that looked
+like:
+
+commands/
+ init.rb
+ add/
+ default.rb
+ rm/
+ default.rb
+
+
+
+
42 Rakefile
@@ -0,0 +1,42 @@
+require 'rake'
+require 'rake/testtask'
+require 'rcov/rcovtask'
+
+begin
+ require 'jeweler'
+ Jeweler::Tasks.new do |s|
+ s.name = "menutree"
+ s.summary = "a simple hierachical command line shell"
+ s.description = "a simple hierachical command line shell"
+ s.email = "mat@geeky.net"
+ s.homepage = "http://github.com/mtrudel/menutree"
+ s.authors = ["Mat Trude", "Grant McInnes"]
+ end
+rescue LoadError
+ puts "Jeweler not available. Install it with: gem install jeweler"
+end
+
+Rake::TestTask.new do |t|
+ t.libs << 'lib'
+ t.pattern = 'test/**/*_test.rb'
+ t.verbose = false
+end
+
+begin
+ require 'yard'
+ YARD::Rake::YardocTask.new(:yardoc)
+rescue LoadError
+ task :yardoc do
+ abort "YARD is not available. In order to run yard, you must: sudo gem install yard"
+ end
+end
+
+
+Rcov::RcovTask.new do |t|
+ t.libs << "test"
+ t.test_files = FileList['test/*_test.rb']
+ t.verbose = true
+end
+
+
+task :default => :rcov
1 VERSION
@@ -0,0 +1 @@
+0.0.1
86 lib/base.rb
@@ -0,0 +1,86 @@
+require 'readline'
+require 'active_support'
+
+class MenuTree
+ attr_accessor :directory
+ attr_accessor :parent
+ attr_accessor :prompt
+
+ def initialize(directory, parent = "")
+ @directory = directory
+ @parent = parent
+ if @parent.is_a? self.class
+ @prompt = @parent.prompt + "/" + File.basename(@directory)
+ else
+ @prompt = @parent
+ end
+ end
+
+ def run_command(cmds)
+ Readline.completion_append_character = " "
+ Readline.completion_proc = Proc.new do |str|
+ commands(str)
+ end
+
+ one_shot = true unless cmds.empty?
+ to_run = cmds.shift
+ while true do
+ if (to_run.nil?)
+ cmds = Readline.readline("#{@prompt}> ", true).split
+ to_run = cmds.shift
+ end
+
+ # If it's a builtin, run it
+ if (to_run.nil?)
+ next
+ elsif (to_run == "help" or to_run == "?")
+ display_help
+ elsif (to_run == "quit")
+ exit
+ elsif (to_run == "..")
+ return
+ elsif File.exists?(File.join(@directory, "#{to_run}.rb"))
+ # If we have a .rb file, run it with the rest of cmd as arguments
+ # TODO -- use .extend?
+ require File.join(@directory, "#{to_run}")
+ eval(to_run.camelize).new.run(cmds)
+ elsif File.directory?(File.join(@directory, to_run))
+ menu = MenuTree.new(File.join(@directory, to_run), self)
+ menu.run_command(cmds)
+ elsif File.exists?(File.join(@directory, "default.rb"))
+ # TODO let default handle it, allowing us to have proxy menus
+ else
+ puts "Unknown command #{to_run}"
+ end
+ to_run = nil
+ return if one_shot
+ end
+ end
+
+ def commands(prefix='')
+ completions = []
+ Dir.new(@directory).each do |command|
+ if command =~ /^[^\.]/ and command =~ /^#{Regexp.escape(prefix)}/ and File.file?(File.join(@directory, command))
+ completions << File.basename(command, '.rb')
+ end
+ end
+ completions
+ end
+
+ def display_help
+ puts <<EOF
+Builtin commands:
+ help:\t\t\t\tDisplays this message
+ quit:\t\t\t\tQuits this session
+ ..: \t\t\t\tGoes up a level in the menu (or quits if at the root)
+
+#{@parent} commands:
+EOF
+
+ # TODO display some help for each submenu
+ commands.each do |cmd|
+ require File.join(@directory, "#{cmd}")
+ puts " #{cmd}:\t\t\t\t#{eval(cmd.camelize).new.description}"
+ end
+ end
+end
35 menutree.gemspec
@@ -0,0 +1,35 @@
+# Generated by jeweler
+# DO NOT EDIT THIS FILE DIRECTLY
+# Instead, edit Jeweler::Tasks in rakefile, and run the gemspec command
+# -*- encoding: utf-8 -*-
+
+Gem::Specification.new do |s|
+ s.name = %q{menutree}
+ s.version = "0.0.1"
+
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
+ s.authors = ["Mat Trude", "Grant McInnes"]
+ s.date = %q{2010-03-15}
+ s.description = %q{a simple hierachical command line shell}
+ s.email = %q{mat@geeky.net}
+ s.extra_rdoc_files = [
+ "README"
+ ]
+ s.has_rdoc = true
+ s.homepage = %q{http://github.com/mtrudel/menutree}
+ s.rdoc_options = ["--charset=UTF-8"]
+ s.require_paths = ["lib"]
+ s.rubygems_version = %q{1.3.1}
+ s.summary = %q{a simple hierachical command line shell}
+
+ if s.respond_to? :specification_version then
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
+ s.specification_version = 2
+
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
+ else
+ end
+ else
+ end
+end
+
5 menutree.rb
@@ -0,0 +1,5 @@
+$:.unshift(File.dirname(__FILE__)) unless
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
+
+require 'lib/base'
+

0 comments on commit fad6338

Please sign in to comment.
Something went wrong with that request. Please try again.