prompt 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.
@@ -0,0 +1,143 @@
1
+ # Prompt
2
+
3
+ ## What is this?
4
+
5
+ Prompt makes it easy to build slick command-line applications with [Tab Completion](#tabcompletion), [Command History](#commandhistory), and [Built-in Help](#built-inhelp)
6
+
7
+ ## Installation
8
+
9
+ gem install prompt
10
+
11
+ ## An example
12
+
13
+ Commands are defined with a Sinatra-inspired DSL:
14
+
15
+ ```ruby
16
+ require 'prompt'
17
+ extend Prompt::DSL
18
+
19
+ desc "Move"
20
+
21
+ variable :direction, "A cardinal direction", %w(north east south west)
22
+
23
+ command "go :direction", "Walk in the specified direction" do |direction|
24
+ puts "You walked #{direction} and were eaten by a grue."
25
+ end
26
+
27
+ desc "Interact"
28
+
29
+ command "look", "Look around" do
30
+ puts "You're in a dark room."
31
+ end
32
+
33
+ command "say :something", "Say something" do |something|
34
+ puts "You say '#{something}'"
35
+ end
36
+
37
+ Prompt::Console.start
38
+ ```
39
+
40
+ ### Tab completion
41
+
42
+ Tab completion is hooked up automatically after you define your commands and variables
43
+
44
+ $ my_app
45
+ > g<TAB>
46
+ > go <TAB>
47
+ go east go north go south go west
48
+ > go n<TAB>
49
+ > go north
50
+
51
+ ### Command history
52
+
53
+ Command history is enabled automatically. You can scroll through the history with the UP and DOWN keys. You can search the history with CTRL-R.
54
+
55
+ You can preserve the history between runs by specifying a history filename when starting the console
56
+
57
+ ```ruby
58
+ history_file = File.join(ENV["HOME"], ".my-history")
59
+ Prompt::Console.start history_file
60
+ ```
61
+
62
+
63
+ ### Built-in help
64
+
65
+ The `help` command is built-in. It will print all of the commands that you've defined in your app.
66
+
67
+ $ my_app
68
+ > help
69
+ Console commands
70
+
71
+ help -v
72
+ help
73
+ exit
74
+
75
+ Move
76
+
77
+ go <direction> Walk in the specified direction
78
+
79
+ Interact
80
+
81
+ look Look around
82
+ say <something> Say something
83
+
84
+ ## Grouping commands
85
+
86
+ You can put commands in logical groups. This only affects how help is printed.
87
+
88
+ ```ruby
89
+ desc "Taco commands"
90
+
91
+ command ...
92
+ command ...
93
+
94
+ desc "Burger commands"
95
+
96
+ command ...
97
+ ```
98
+
99
+ ## Using Variables
100
+
101
+ Variables can be used in a command.
102
+
103
+ ```ruby
104
+ command "name :first :last" do |first, last|
105
+ puts "Hi #{first} #{last}"
106
+ end
107
+ ```
108
+
109
+ Here, the variables are named `first` and `last`. Their values are be passed as arguments to the command's block, in the order in which they appear.
110
+
111
+ ### Defining variables
112
+
113
+ It's not necessary to define a variable before using it in a command, but doing so will allow you to provide a useful description and valid values for the variable.
114
+
115
+ ```ruby
116
+ variable :name, "Description"
117
+ ```
118
+
119
+ ### Specifying a static list of valid values
120
+
121
+ You can specify a static list of valid values for a variable. These will be expanded when using tab completion.
122
+
123
+ ```ruby
124
+ variable :name, "Description", %w(value1 value2)
125
+ ```
126
+
127
+ ### Specifying a dynamic list of valid values
128
+
129
+ Instead of specifying a static list, you can specify a block that will dynamically return a list of valid values for a variable. These will be expanded when using tab completion.
130
+
131
+ ```ruby
132
+ dynamic_variable :file, "JPG file" do
133
+ Dir.glob "*.jpg"
134
+ end
135
+ ```
136
+
137
+ ## Configuration options
138
+
139
+ The default prompt `"> "` can be changed before starting the console.
140
+
141
+ ```ruby
142
+ Prompt::Console.prompt = "#{Dir.pwd}> "
143
+ ```
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'prompt'
4
+
5
+ extend Prompt::DSL
6
+
7
+ @pwd = File.absolute_path Dir.pwd
8
+
9
+ dynamic_variable :dir, "Directory" do
10
+ Dir.entries(@pwd).select do |e|
11
+ File.directory? e
12
+ end
13
+ end
14
+
15
+ command "cd :dir", "Change directory" do |dir|
16
+ @pwd = File.absolute_path File.join(@pwd, dir)
17
+ end
18
+
19
+ command "ls", "List files" do
20
+ puts @pwd
21
+ puts Dir.entries(@pwd)
22
+ end
23
+
24
+ command "pwd", "Print current directory" do
25
+ puts @pwd
26
+ end
27
+
28
+ Prompt::Console.start
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'prompt'
4
+
5
+ extend Prompt::DSL
6
+
7
+ GRUE = 3
8
+ @moves = 0
9
+
10
+ desc "Move"
11
+
12
+ variable :direction, "A cardinal direction", %w(north east south west)
13
+
14
+ command "go :direction", "Walk in the specified direction" do |direction|
15
+ puts "You walked #{direction}"
16
+ @moves += 1
17
+ if @moves > GRUE
18
+ puts "You have been eaten by a grue"
19
+ exit
20
+ end
21
+ end
22
+
23
+ desc "Interact"
24
+
25
+ command "look", "Look around" do
26
+ if @moves < GRUE
27
+ puts "You're in a nice, well-lit room"
28
+ else
29
+ puts "It is pitch black. You are likely to be eaten by a grue."
30
+ end
31
+ end
32
+
33
+ command "say :something", "Say something" do |something|
34
+ puts "You say '#{something}'"
35
+ end
36
+
37
+ Prompt::Console.start
@@ -0,0 +1,5 @@
1
+ require 'prompt/application'
2
+ require 'prompt/dsl'
3
+ require 'prompt/prompt_module'
4
+ require 'prompt/command_not_found'
5
+ require 'prompt/console'
@@ -0,0 +1,53 @@
1
+ require 'prompt/command_group'
2
+ require 'prompt/command'
3
+
4
+ module Prompt
5
+ class Application
6
+ attr :command_groups
7
+
8
+ def initialize
9
+ @command_groups = []
10
+ end
11
+
12
+ def use_command_group desc
13
+ @current_command_group_name = desc
14
+ end
15
+
16
+ def define_command name, desc = nil, variables, &block
17
+ current_command_group.commands << Command.new(name, desc, variables, &block)
18
+ end
19
+
20
+ def exec command_str
21
+ commands.each do |command|
22
+ args = command.match(command_str)
23
+ return command.run(args) if args
24
+ end
25
+ raise CommandNotFound.new(command_str)
26
+ end
27
+
28
+ def completions starting_with = nil
29
+ return all_expansions unless starting_with
30
+
31
+ all_expansions.grep /^#{Regexp.escape(starting_with)}/
32
+ end
33
+
34
+ private
35
+
36
+ def commands
37
+ @command_groups.map(&:commands).reduce [] { |a, b| a + b }
38
+ end
39
+
40
+ def all_expansions
41
+ commands.map(&:expansions).flatten
42
+ end
43
+
44
+ def current_command_group
45
+ command_groups.find { |cg| cg.name == @current_command_group_name } || begin
46
+ cg = CommandGroup.new(@current_command_group_name)
47
+ @command_groups << cg
48
+ cg
49
+ end
50
+ end
51
+
52
+ end
53
+ end
@@ -0,0 +1,95 @@
1
+ module Prompt
2
+ class Command
3
+
4
+ attr :name
5
+ attr :desc
6
+
7
+ def initialize(name, desc = nil, all_variables = [], &block)
8
+ @name = name
9
+ @desc = desc
10
+ @all_variables = all_variables
11
+ @action = block
12
+ end
13
+
14
+ def run args
15
+ @action.call *args
16
+ end
17
+
18
+ def match(str)
19
+ if m = regex.match(str.strip)
20
+ m[1..-1]
21
+ end
22
+ end
23
+
24
+ def variables
25
+ @variables ||= words.select {|w| w.kind_of? Variable}
26
+ end
27
+
28
+ def expansions
29
+ expand words
30
+ end
31
+
32
+ def usage
33
+ words.map do |word|
34
+ case word
35
+ when Variable
36
+ "<#{word.name}>"
37
+ else
38
+ word
39
+ end
40
+ end.join(" ")
41
+ end
42
+
43
+ private
44
+
45
+ def regex
46
+ @regex ||= begin
47
+ regex_strs = words.map do |word|
48
+ case word
49
+ when Variable
50
+ word.regex
51
+ else
52
+ Regexp.escape(word)
53
+ end
54
+ end
55
+ Regexp.new("^#{regex_strs.join("\s+")}$")
56
+ end
57
+ end
58
+
59
+ def words
60
+ @words ||= @name.split(/\s/).map do |word|
61
+ if word[0] == ":"
62
+ sym = word[1..-1].to_sym
63
+ @all_variables.find {|v| v.name == sym} || Variable.new(sym, sym.to_s)
64
+ else
65
+ word
66
+ end
67
+ end
68
+ end
69
+
70
+ def expand a
71
+ return [] if a.empty?
72
+
73
+ head = a[0]
74
+ tail = a[1..-1]
75
+
76
+ case head
77
+ when Variable
78
+ head = head.expansions
79
+ else
80
+ head = [head]
81
+ end
82
+
83
+ return head if tail.empty?
84
+
85
+ result = []
86
+ head.each do |h|
87
+ expand(tail).each do |e|
88
+ result << "#{h} #{e}"
89
+ end
90
+ end
91
+ result
92
+ end
93
+
94
+ end
95
+ end
@@ -0,0 +1,12 @@
1
+ module Prompt
2
+ class CommandGroup
3
+ attr_accessor :name
4
+ attr :commands
5
+
6
+ def initialize name
7
+ @name = name
8
+ @commands = []
9
+ end
10
+
11
+ end
12
+ end
@@ -0,0 +1,7 @@
1
+ module Prompt
2
+ class CommandNotFound < RuntimeError
3
+ def initialize(command_str)
4
+ super "Command not found: #{command_str}"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,2 @@
1
+ require 'prompt/console/console_module'
2
+ require 'prompt/console/builtins'
@@ -0,0 +1,44 @@
1
+ require 'prompt/dsl'
2
+
3
+ module Prompt
4
+ module Console
5
+
6
+ class Builtins
7
+ extend DSL
8
+
9
+ desc "Console commands"
10
+
11
+ command "help -v" do
12
+ print_help true
13
+ end
14
+
15
+ command "help" do
16
+ print_help
17
+ end
18
+
19
+ command "exit" do
20
+ exit
21
+ end
22
+
23
+ private
24
+
25
+ def self.print_help verbose = false
26
+ Prompt.application.command_groups.each do |cg|
27
+ puts
28
+ puts cg.name
29
+ puts
30
+ cg.commands.each do |cmd|
31
+ puts " %-40s %s" % [cmd.usage, cmd.desc]
32
+ if verbose
33
+ cmd.variables.each do |v|
34
+ puts " "*43 + ("%-10s %s" % ["<#{v.name}>", "#{v.desc}"])
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ end # class Builtins
42
+
43
+ end
44
+ end
@@ -0,0 +1,66 @@
1
+ require 'readline'
2
+ require 'prompt'
3
+
4
+ module Prompt
5
+ module Console
6
+
7
+ HISTORY_MAX_SIZE = 100
8
+ PROMPT = "> "
9
+
10
+ CompletionProc = proc do |s|
11
+ Prompt.application.completions(s)
12
+ end
13
+
14
+ def self.start(history_file = nil)
15
+ @prompt = PROMPT
16
+
17
+ # Store the state of the terminal
18
+ stty_save = `stty -g`.chomp
19
+ # and restore it when exiting
20
+ at_exit do
21
+ system('stty', stty_save)
22
+ save_history history_file if history_file
23
+ end
24
+
25
+ Readline.basic_word_break_characters = ""
26
+ Readline.completion_append_character = " "
27
+ Readline.completion_proc = CompletionProc
28
+
29
+ load_history history_file if history_file
30
+
31
+ while line = Readline.readline(@prompt, true)
32
+ begin
33
+ Prompt.application.exec(line).nil?
34
+ rescue ::Prompt::CommandNotFound => e
35
+ STDERR.puts e.message
36
+ end
37
+ end
38
+ end
39
+
40
+ def self.prompt= prompt
41
+ @prompt = prompt
42
+ end
43
+
44
+ def self.prompt
45
+ @prompt
46
+ end
47
+
48
+ def self.save_history file
49
+ history_no_dups = Readline::HISTORY.to_a.reverse.uniq[0,HISTORY_MAX_SIZE].reverse
50
+ File.open(file, "w") do |f|
51
+ history_no_dups.each do |line|
52
+ f.puts line
53
+ end
54
+ end
55
+ end
56
+
57
+ def self.load_history file
58
+ if File.exist? file
59
+ File.readlines(file).each do |line|
60
+ Readline::HISTORY.push line.strip
61
+ end
62
+ end
63
+ end
64
+
65
+ end # module Console
66
+ end
@@ -0,0 +1,37 @@
1
+ require 'prompt/variable'
2
+ require 'prompt/proc_variable'
3
+
4
+ module Prompt
5
+ module DSL
6
+
7
+ def self.extended(base)
8
+ name = if base.respond_to? :name
9
+ base.name
10
+ else
11
+ "Commands"
12
+ end
13
+ Prompt.application.use_command_group(name)
14
+ end
15
+
16
+ def desc desc
17
+ Prompt.application.use_command_group(desc)
18
+ end
19
+
20
+ def command(name, desc = nil, &block)
21
+ Prompt.application.define_command(name, desc, @variables || {}, &block)
22
+ end
23
+
24
+ def variable(name, desc, values = nil)
25
+ @variables = [] unless defined? @variables
26
+ raise "variable :#{name} is already defined" if @variables.find {|v| v.name == name}
27
+ @variables << Variable.new(name, desc, values)
28
+ end
29
+
30
+ def dynamic_variable(name, desc, &block)
31
+ @variables = [] unless defined? @variables
32
+ raise "variable :#{name} is already defined" if @variables.find {|v| v.name == name}
33
+ @variables << ProcVariable.new(name, desc, &block)
34
+ end
35
+
36
+ end
37
+ end
@@ -0,0 +1,14 @@
1
+ module Prompt
2
+ class ProcVariable < Variable
3
+
4
+ def initialize(name, desc, &block)
5
+ super(name, desc, nil)
6
+ @proc = block
7
+ end
8
+
9
+ def values
10
+ @proc.call
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,10 @@
1
+ module Prompt
2
+ class << self
3
+
4
+ # Singleton instance
5
+ #
6
+ def application
7
+ @application ||= Application.new
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,27 @@
1
+ module Prompt
2
+ class Variable
3
+
4
+ attr :name
5
+ attr :desc
6
+ attr :values
7
+
8
+ def initialize(name, desc, values = nil)
9
+ @name = name
10
+ @desc = desc
11
+ @values = values
12
+ end
13
+
14
+ def regex
15
+ if values
16
+ "(#{values.map{|v| Regexp.escape(v)}.join("|")})"
17
+ else
18
+ "([^\s]+)"
19
+ end
20
+ end
21
+
22
+ def expansions
23
+ values || ["<#{name}>"]
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,105 @@
1
+ require 'prompt/command'
2
+ require 'prompt/variable'
3
+
4
+ include Prompt
5
+
6
+ describe Prompt::Command do
7
+
8
+ DIRECTIONS = %w{n e s w}
9
+ SPEEDS = %w{quickly slowly}
10
+
11
+ describe "#match" do
12
+ it "matches correctly" do
13
+ c = Command.new("hi")
14
+
15
+ c.match("hi").should == []
16
+ c.match("hi ").should == []
17
+ c.match(" hi").should == []
18
+
19
+ c.match("bye").should be_nil
20
+ c.match("h").should be_nil
21
+ c.match("").should be_nil
22
+ end
23
+
24
+ it "matches correctly with a variable" do
25
+ c = Command.new("hi :name")
26
+
27
+ c.match("hi guy").should == ["guy"]
28
+ c.match("higuy").should be_nil
29
+ c.match("higuy guy").should be_nil
30
+ end
31
+
32
+ it "matches correctly with a variable and multiple spaces" do
33
+ c = Command.new("hi :name")
34
+
35
+ c.match(" hi guy").should == ["guy"]
36
+ c.match("hi guy").should == ["guy"]
37
+ c.match("hi guy ").should == ["guy"]
38
+ end
39
+
40
+ it "matches correctly with 2 variables" do
41
+ c = Command.new("hi :first :last")
42
+
43
+ c.match("hi agent smith").should == ["agent", "smith"]
44
+ c.match("hi agent").should be_nil
45
+ c.match("hi agent smith guy").should be_nil
46
+ end
47
+
48
+ it "matches correctly with variable value constraint" do
49
+ v = [Variable.new(:dir, "", DIRECTIONS)]
50
+ c = Command.new("go :dir", nil, v)
51
+
52
+ c.match("go n").should == ["n"]
53
+ c.match("go s").should == ["s"]
54
+ c.match("go x").should be_nil
55
+ c.match("go nn").should be_nil
56
+ end
57
+ end
58
+
59
+ describe "#variables" do
60
+ it "returns correctly" do
61
+ color = Variable.new(:color, "")
62
+ flavor = Variable.new(:flavor, "")
63
+
64
+ Command.new("one").variables.should == []
65
+ vs = Command.new("one :color").variables
66
+ vs.length.should == 1
67
+ vs.first.name.should == :color
68
+ Command.new("one :color", nil, [color]).variables.should == [color]
69
+ Command.new("one :color", nil, [color, flavor]).variables.should == [color]
70
+ end
71
+ end
72
+
73
+ describe "#expansions" do
74
+ it "expands correctly with no variables" do
75
+ Command.new("one").expansions.should == ["one"]
76
+ end
77
+
78
+ it "expands correctly with undefined variables" do
79
+ Command.new("go :dir").expansions.should == ["go <dir>"]
80
+ end
81
+
82
+ it "expands correctly with defined variables" do
83
+ v = [Variable.new(:dir, "", DIRECTIONS), Variable.new(:speed, "", SPEEDS)]
84
+ Command.new("go :dir", nil, v).expansions.should == ["go n", "go e", "go s", "go w"]
85
+ Command.new("go :dir :speed", nil, v).expansions.should ==
86
+ ["go n quickly", "go n slowly",
87
+ "go e quickly", "go e slowly",
88
+ "go s quickly", "go s slowly",
89
+ "go w quickly", "go w slowly"]
90
+ end
91
+ end
92
+
93
+ describe "#usage" do
94
+ it "returns correctly" do
95
+ color = Variable.new(:color, "")
96
+
97
+ Command.new("one").usage.should == "one"
98
+ Command.new("one :color").usage.should == "one <color>"
99
+ Command.new("one :color", nil, [color]).usage.should == "one <color>"
100
+ Command.new("one :color three").usage.should == "one <color> three"
101
+ end
102
+ end
103
+
104
+ end
105
+
metadata ADDED
@@ -0,0 +1,61 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: prompt
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Mike Smith
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-02-17 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: Prompt makes it easy to build slick command-line applications with Tab
15
+ Completion, Command History, and Built-in Help
16
+ email: mike@sticknet.net
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - README.md
22
+ - lib/prompt/application.rb
23
+ - lib/prompt/command.rb
24
+ - lib/prompt/command_group.rb
25
+ - lib/prompt/command_not_found.rb
26
+ - lib/prompt/console/builtins.rb
27
+ - lib/prompt/console/console_module.rb
28
+ - lib/prompt/console.rb
29
+ - lib/prompt/dsl.rb
30
+ - lib/prompt/proc_variable.rb
31
+ - lib/prompt/prompt_module.rb
32
+ - lib/prompt/variable.rb
33
+ - lib/prompt.rb
34
+ - spec/prompt/command_spec.rb
35
+ - examples/file_manager
36
+ - examples/mud
37
+ homepage: http://github.com/mudynamics/prompt
38
+ licenses: []
39
+ post_install_message:
40
+ rdoc_options: []
41
+ require_paths:
42
+ - lib
43
+ required_ruby_version: !ruby/object:Gem::Requirement
44
+ none: false
45
+ requirements:
46
+ - - ! '>='
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ required_rubygems_version: !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: 1.3.6
55
+ requirements: []
56
+ rubyforge_project:
57
+ rubygems_version: 1.8.10
58
+ signing_key:
59
+ specification_version: 3
60
+ summary: A small framework that makes it easy to build slick command-line applications
61
+ test_files: []