prompt 0.0.3 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +7 -0
- data/README.md +12 -14
- data/examples/file_manager +6 -4
- data/examples/mud +2 -2
- data/lib/prompt/application.rb +23 -8
- data/lib/prompt/command.rb +38 -52
- data/lib/prompt/command_not_found.rb +0 -3
- data/lib/prompt/console/builtins.rb +1 -1
- data/lib/prompt/console/console_module.rb +39 -7
- data/lib/prompt/dsl.rb +6 -8
- data/lib/prompt/dsl_helper.rb +18 -15
- data/lib/prompt/matcher.rb +13 -0
- data/lib/prompt/multi_matcher.rb +21 -0
- data/lib/prompt/parameter.rb +10 -18
- data/lib/prompt/prompt_module.rb +1 -1
- data/lib/prompt/simple_matcher.rb +13 -0
- data/spec/prompt/command_spec.rb +78 -71
- data/spec/prompt/console_spec.rb +79 -0
- data/spec/spec_helper.rb +17 -0
- metadata +10 -7
- data/lib/prompt/glob_parameter.rb +0 -17
- data/lib/prompt/proc_parameter.rb +0 -18
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,13 @@
|
|
2
2
|
|
3
3
|
This is updated when a new version is pushed to http://rubygems.org
|
4
4
|
|
5
|
+
## 0.1.0 (Mar 8, 2011)
|
6
|
+
|
7
|
+
* DSL change: `desc` was renamed to `group`
|
8
|
+
* Tab completion will now list only the completions for the current word, instead of
|
9
|
+
the whole command. This makes it behave like bash and works much better with `*params`
|
10
|
+
* Hitting ENTER with an empty line will no longer print a Command Not Found error
|
11
|
+
|
5
12
|
## 0.0.3 (Feb 22, 2011)
|
6
13
|
|
7
14
|
* DSL change: `variable` & `dynamic_variable` were renamed to `param`
|
data/README.md
CHANGED
@@ -16,7 +16,7 @@ Commands are defined with a Sinatra-inspired DSL:
|
|
16
16
|
require 'prompt'
|
17
17
|
extend Prompt::DSL
|
18
18
|
|
19
|
-
|
19
|
+
group "Move"
|
20
20
|
|
21
21
|
param :direction, "A cardinal direction", %w(north east south west)
|
22
22
|
|
@@ -24,7 +24,7 @@ command "go :direction", "Walk in the specified direction" do |direction|
|
|
24
24
|
puts "You walked #{direction} and were eaten by a grue."
|
25
25
|
end
|
26
26
|
|
27
|
-
|
27
|
+
group "Interact"
|
28
28
|
|
29
29
|
command "look", "Look around" do
|
30
30
|
puts "You're in a dark room."
|
@@ -44,7 +44,7 @@ Tab completion is hooked up automatically after you define your commands and par
|
|
44
44
|
$ my_app
|
45
45
|
> g<TAB>
|
46
46
|
> go <TAB>
|
47
|
-
|
47
|
+
east north south west
|
48
48
|
> go n<TAB>
|
49
49
|
> go north
|
50
50
|
|
@@ -86,12 +86,12 @@ The `help` command is built-in. It will print all of the commands that you've d
|
|
86
86
|
You can put commands in logical groups. This only affects how help is printed.
|
87
87
|
|
88
88
|
```ruby
|
89
|
-
|
89
|
+
group "File commands"
|
90
90
|
|
91
91
|
command ...
|
92
92
|
command ...
|
93
93
|
|
94
|
-
|
94
|
+
group "Directory commands"
|
95
95
|
|
96
96
|
command ...
|
97
97
|
```
|
@@ -112,30 +112,28 @@ Here, the parameters are named `first` and `last`. Their values are be passed a
|
|
112
112
|
Each `:parameter` only matches a single word. If you want to match multiple words to one parameter, use a `*parameter`.
|
113
113
|
|
114
114
|
```ruby
|
115
|
-
command "
|
116
|
-
puts "You
|
115
|
+
command "cp *file :dest" do |files, dest|
|
116
|
+
puts "You copied #{files.length} files to #{dest}"
|
117
117
|
end
|
118
118
|
```
|
119
119
|
|
120
120
|
### Defining parameters
|
121
121
|
|
122
|
-
It's not necessary to define a parameter before using it in a command, but doing so will allow you to provide a useful description and
|
122
|
+
It's not necessary to define a parameter before using it in a command, but doing so will allow you to provide a useful description and list of possible completions for the parameter.
|
123
123
|
|
124
124
|
```ruby
|
125
125
|
param :name, "Description"
|
126
126
|
```
|
127
127
|
|
128
|
-
### Specifying
|
128
|
+
### Specifying parameter completions
|
129
129
|
|
130
|
-
You can specify
|
130
|
+
You can specify the completions for a parameter as a static list:
|
131
131
|
|
132
132
|
```ruby
|
133
|
-
param :
|
133
|
+
param :color, "A color", %w(red green blue)
|
134
134
|
```
|
135
135
|
|
136
|
-
|
137
|
-
|
138
|
-
Instead of a static list, you can specify a block that will dynamically return a list of valid values for a parameter. These will also be expanded when using tab completion.
|
136
|
+
or as a dynamically-generated one:
|
139
137
|
|
140
138
|
```ruby
|
141
139
|
param :file, "JPG file" do
|
data/examples/file_manager
CHANGED
@@ -8,7 +8,7 @@ def change_prompt dir
|
|
8
8
|
Prompt.application.prompt = "#{@pwd}> "
|
9
9
|
end
|
10
10
|
|
11
|
-
@pwd = File.
|
11
|
+
@pwd = File.expand_path Dir.pwd
|
12
12
|
change_prompt @pwd
|
13
13
|
|
14
14
|
param :dir, "Directory" do
|
@@ -18,7 +18,7 @@ param :dir, "Directory" do
|
|
18
18
|
end
|
19
19
|
|
20
20
|
command "cd :dir", "Change directory" do |dir|
|
21
|
-
@pwd = File.
|
21
|
+
@pwd = File.expand_path File.join(@pwd, dir)
|
22
22
|
change_prompt @pwd
|
23
23
|
end
|
24
24
|
|
@@ -38,9 +38,11 @@ command "pwd", "Print current directory" do
|
|
38
38
|
puts @pwd
|
39
39
|
end
|
40
40
|
|
41
|
-
param :
|
41
|
+
param :file, "File" do
|
42
|
+
Dir.entries(@pwd)
|
43
|
+
end
|
42
44
|
|
43
|
-
command "cp *
|
45
|
+
command "cp *file :dir", "Copy one or more files to dest" do |files, dir|
|
44
46
|
puts "Copying #{files.length} file(s) to #{dir}"
|
45
47
|
# doesn't actually do it...
|
46
48
|
end
|
data/examples/mud
CHANGED
@@ -7,7 +7,7 @@ extend Prompt::DSL
|
|
7
7
|
GRUE = 3
|
8
8
|
@moves = 0
|
9
9
|
|
10
|
-
|
10
|
+
group "Move"
|
11
11
|
|
12
12
|
param :direction, "A cardinal direction", %w(north east south west)
|
13
13
|
|
@@ -20,7 +20,7 @@ command "go :direction", "Walk in the specified direction" do |direction|
|
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
-
|
23
|
+
group "Interact"
|
24
24
|
|
25
25
|
command "look", "Look around" do
|
26
26
|
if @moves < GRUE
|
data/lib/prompt/application.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
require 'prompt/command_group'
|
2
2
|
require 'prompt/command'
|
3
|
+
require 'prompt/matcher'
|
4
|
+
require 'prompt/simple_matcher'
|
5
|
+
require 'prompt/multi_matcher'
|
3
6
|
|
4
7
|
module Prompt
|
5
8
|
class Application
|
@@ -11,7 +14,7 @@ module Prompt
|
|
11
14
|
@prompt = "> "
|
12
15
|
end
|
13
16
|
|
14
|
-
def
|
17
|
+
def select_group desc
|
15
18
|
@current_command_group_name = desc
|
16
19
|
end
|
17
20
|
|
@@ -19,22 +22,32 @@ module Prompt
|
|
19
22
|
current_command_group.commands << command
|
20
23
|
end
|
21
24
|
|
22
|
-
def exec
|
25
|
+
def exec words
|
23
26
|
commands.each do |command|
|
24
|
-
args = command.match(
|
27
|
+
args = command.match(words)
|
25
28
|
return command.run(args) if args
|
26
29
|
end
|
27
|
-
raise CommandNotFound.new
|
30
|
+
raise CommandNotFound.new
|
28
31
|
ensure
|
29
32
|
clear_cached_values
|
30
33
|
end
|
31
34
|
|
32
|
-
def completions
|
33
|
-
|
35
|
+
def completions line_starting_with, word_starting_with
|
36
|
+
args = Console.split(line_starting_with)
|
37
|
+
last_idx = index_of_last_word(line_starting_with)
|
38
|
+
all_expansions(args[0,last_idx], word_starting_with)
|
34
39
|
end
|
35
40
|
|
36
41
|
private
|
37
42
|
|
43
|
+
def index_of_last_word line
|
44
|
+
ss = StringScanner.new(line)
|
45
|
+
ss.scan(/\s+/)
|
46
|
+
idx = 0
|
47
|
+
idx += 1 while ss.scan(/[^\s]+\s+/)
|
48
|
+
idx
|
49
|
+
end
|
50
|
+
|
38
51
|
def clear_cached_values
|
39
52
|
commands.each do |c|
|
40
53
|
c.clear_cached_values
|
@@ -45,8 +58,10 @@ module Prompt
|
|
45
58
|
@command_groups.map(&:commands).flatten(1)
|
46
59
|
end
|
47
60
|
|
48
|
-
def all_expansions
|
49
|
-
commands.map
|
61
|
+
def all_expansions(args, partial_arg)
|
62
|
+
commands.select { |c| c.could_match? args }.map do |c|
|
63
|
+
c.expansions(args.length, partial_arg)
|
64
|
+
end.flatten(1)
|
50
65
|
end
|
51
66
|
|
52
67
|
def current_command_group
|
data/lib/prompt/command.rb
CHANGED
@@ -1,13 +1,11 @@
|
|
1
1
|
require 'strscan'
|
2
2
|
require 'prompt/parameter'
|
3
|
+
require 'prompt/console/console_module'
|
3
4
|
|
4
5
|
module Prompt
|
5
6
|
class Command
|
6
7
|
|
7
8
|
SEP = "\036" # RS - record separator
|
8
|
-
S_QUOTED_ARG = /'([^']*)'/
|
9
|
-
D_QUOTED_ARG = /"([^"]*)"/
|
10
|
-
UNQUOTED_ARG = /[^\s]+/
|
11
9
|
|
12
10
|
attr :name
|
13
11
|
attr :desc
|
@@ -19,28 +17,27 @@ module Prompt
|
|
19
17
|
@action = block
|
20
18
|
|
21
19
|
@name = words.join(" ")
|
22
|
-
@
|
20
|
+
@matchers = words.select {|w| w.kind_of?(Matcher) }
|
21
|
+
@parameters = @matchers.map { |m| m.parameter }
|
23
22
|
end
|
24
23
|
|
25
24
|
def run args
|
26
25
|
@action.call *args
|
27
26
|
end
|
28
27
|
|
29
|
-
def match
|
30
|
-
if
|
31
|
-
@
|
28
|
+
def match words
|
29
|
+
if args = regex.match(words.join(SEP))
|
30
|
+
@matchers.map {|m| m.matches(args[m.parameter.name]) }
|
32
31
|
end
|
33
32
|
end
|
34
33
|
|
35
|
-
def expansions
|
36
|
-
expand @words
|
37
|
-
end
|
38
|
-
|
39
34
|
def usage
|
40
35
|
@words.map do |word|
|
41
36
|
case word
|
42
|
-
when
|
43
|
-
"<#{word.name}>"
|
37
|
+
when MultiMatcher
|
38
|
+
"<#{word.parameter.name}> ..."
|
39
|
+
when Matcher
|
40
|
+
"<#{word.parameter.name}>"
|
44
41
|
else
|
45
42
|
word
|
46
43
|
end
|
@@ -53,30 +50,43 @@ module Prompt
|
|
53
50
|
end
|
54
51
|
end
|
55
52
|
|
56
|
-
|
53
|
+
def expansions(word_idx, starting_with)
|
54
|
+
# TODO - combine glob parameters with any that follow
|
55
|
+
word = @words[0..word_idx].find { |w| w.kind_of? MultiMatcher }
|
56
|
+
word = word || @words[word_idx]
|
57
|
+
|
58
|
+
return [] if word.nil?
|
59
|
+
|
60
|
+
case word
|
61
|
+
when Matcher
|
62
|
+
word.parameter.expansions(starting_with)
|
63
|
+
when String
|
64
|
+
word.start_with?(starting_with) ? [word] : []
|
65
|
+
end
|
66
|
+
end
|
57
67
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
args << arg
|
68
|
+
def could_match?(words)
|
69
|
+
words.each_with_index do |w, i|
|
70
|
+
word = @words[i]
|
71
|
+
case word
|
72
|
+
when nil
|
73
|
+
return false
|
74
|
+
when MultiMatcher
|
75
|
+
return true
|
76
|
+
when String
|
77
|
+
return false unless word.start_with?(w)
|
69
78
|
end
|
70
|
-
ss.scan(/\s+/)
|
71
79
|
end
|
72
|
-
|
80
|
+
return true
|
73
81
|
end
|
74
82
|
|
83
|
+
private
|
84
|
+
|
75
85
|
def regex
|
76
86
|
begin
|
77
87
|
regex_strs = @words.map do |word|
|
78
88
|
case word
|
79
|
-
when
|
89
|
+
when Matcher
|
80
90
|
word.regex
|
81
91
|
else
|
82
92
|
Regexp.escape(word)
|
@@ -86,29 +96,5 @@ module Prompt
|
|
86
96
|
end
|
87
97
|
end
|
88
98
|
|
89
|
-
def expand a
|
90
|
-
return [] if a.empty?
|
91
|
-
|
92
|
-
head = a[0]
|
93
|
-
tail = a[1..-1]
|
94
|
-
|
95
|
-
case head
|
96
|
-
when Parameter
|
97
|
-
head = head.expansions
|
98
|
-
else
|
99
|
-
head = [head]
|
100
|
-
end
|
101
|
-
|
102
|
-
return head if tail.empty?
|
103
|
-
|
104
|
-
result = []
|
105
|
-
head.each do |h|
|
106
|
-
expand(tail).each do |e|
|
107
|
-
result << "#{h} #{e}"
|
108
|
-
end
|
109
|
-
end
|
110
|
-
result
|
111
|
-
end
|
112
|
-
|
113
99
|
end
|
114
100
|
end
|
@@ -1,13 +1,15 @@
|
|
1
1
|
require 'readline'
|
2
2
|
require 'prompt'
|
3
|
+
require 'strscan'
|
3
4
|
|
4
5
|
module Prompt
|
5
6
|
module Console
|
6
7
|
|
7
8
|
HISTORY_MAX_SIZE = 100
|
8
9
|
|
9
|
-
CompletionProc = proc do |
|
10
|
-
|
10
|
+
CompletionProc = proc do |word_starting_with|
|
11
|
+
line_starting_with = Readline.line_buffer[0...Readline.point]
|
12
|
+
Prompt.application.completions(line_starting_with, word_starting_with)
|
11
13
|
end
|
12
14
|
|
13
15
|
def self.start(history_file = nil)
|
@@ -19,17 +21,17 @@ module Prompt
|
|
19
21
|
save_history history_file if history_file
|
20
22
|
end
|
21
23
|
|
22
|
-
Readline.basic_word_break_characters = ""
|
23
|
-
Readline.completion_append_character = " "
|
24
24
|
Readline.completion_proc = CompletionProc
|
25
25
|
|
26
26
|
load_history history_file if history_file
|
27
27
|
|
28
28
|
while line = Readline.readline(Prompt.application.prompt, true)
|
29
29
|
begin
|
30
|
-
|
31
|
-
|
32
|
-
|
30
|
+
words = split(line)
|
31
|
+
next if words == []
|
32
|
+
Prompt.application.exec words
|
33
|
+
rescue CommandNotFound
|
34
|
+
STDERR.puts "Command not found: #{line}"
|
33
35
|
end
|
34
36
|
end
|
35
37
|
end
|
@@ -51,5 +53,35 @@ module Prompt
|
|
51
53
|
end
|
52
54
|
end
|
53
55
|
|
56
|
+
private
|
57
|
+
|
58
|
+
S_QUOTED_ARG = /'([^']*)'/
|
59
|
+
D_QUOTED_ARG = /"([^"]*)"/
|
60
|
+
UNQUOTED_ARG = /[^\s]+/
|
61
|
+
|
62
|
+
# Splits a command string into an argument (word) list.
|
63
|
+
# This understands how to make "quoted strings" into a single word
|
64
|
+
def self.split command
|
65
|
+
args = []
|
66
|
+
ss = StringScanner.new(command)
|
67
|
+
ss.scan(/\s+/)
|
68
|
+
until ss.eos?
|
69
|
+
arg = ""
|
70
|
+
while true
|
71
|
+
segment = if ss.scan(S_QUOTED_ARG) or ss.scan(D_QUOTED_ARG)
|
72
|
+
ss[1]
|
73
|
+
else
|
74
|
+
ss.scan(UNQUOTED_ARG)
|
75
|
+
end
|
76
|
+
break unless segment
|
77
|
+
arg << segment
|
78
|
+
end
|
79
|
+
args << arg
|
80
|
+
ss.scan(/\s+/)
|
81
|
+
end
|
82
|
+
args
|
83
|
+
end
|
84
|
+
|
85
|
+
|
54
86
|
end # module Console
|
55
87
|
end
|
data/lib/prompt/dsl.rb
CHANGED
@@ -1,6 +1,4 @@
|
|
1
1
|
require 'prompt/parameter'
|
2
|
-
require 'prompt/proc_parameter'
|
3
|
-
require 'prompt/glob_parameter'
|
4
2
|
require 'prompt/dsl_helper'
|
5
3
|
|
6
4
|
module Prompt
|
@@ -12,11 +10,11 @@ module Prompt
|
|
12
10
|
else
|
13
11
|
"Commands"
|
14
12
|
end
|
15
|
-
Prompt.application.
|
13
|
+
Prompt.application.select_group(name)
|
16
14
|
end
|
17
15
|
|
18
|
-
def
|
19
|
-
Prompt.application.
|
16
|
+
def group desc
|
17
|
+
Prompt.application.select_group(desc)
|
20
18
|
end
|
21
19
|
|
22
20
|
def command(name, desc = nil, &block)
|
@@ -28,10 +26,10 @@ module Prompt
|
|
28
26
|
def param(name, desc, values = nil, &block)
|
29
27
|
@parameters = [] unless defined? @parameters
|
30
28
|
raise "parameter :#{name} is already defined" if @parameters.find {|v| v.name == name}
|
31
|
-
if block
|
32
|
-
|
29
|
+
@parameters << if block
|
30
|
+
Parameter.new(name, desc, &block)
|
33
31
|
else
|
34
|
-
|
32
|
+
Parameter.new(name, desc, values)
|
35
33
|
end
|
36
34
|
end
|
37
35
|
|
data/lib/prompt/dsl_helper.rb
CHANGED
@@ -1,25 +1,28 @@
|
|
1
|
+
require 'prompt/simple_matcher'
|
2
|
+
require 'prompt/multi_matcher'
|
3
|
+
|
1
4
|
module Prompt
|
2
5
|
module DSLHelper
|
3
6
|
|
4
|
-
# Split command into an array of Strings and
|
7
|
+
# Split command into an array of Strings and Matchers
|
5
8
|
def self.words command_name, parameters
|
6
9
|
command_name.strip.split(/\s+/).map do |word|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
desc = param ? param.desc : nil
|
17
|
-
GlobParameter.new(sym, desc)
|
18
|
-
else
|
19
|
-
word
|
20
|
-
end
|
10
|
+
if word[0] == ":"
|
11
|
+
param = find_param word, parameters
|
12
|
+
SimpleMatcher.new param
|
13
|
+
elsif word[0] == "*"
|
14
|
+
param = find_param word, parameters
|
15
|
+
MultiMatcher.new param
|
16
|
+
else
|
17
|
+
word
|
18
|
+
end
|
21
19
|
end
|
22
20
|
end
|
23
21
|
|
22
|
+
def self.find_param word, parameters
|
23
|
+
sym = word[1..-1].to_sym
|
24
|
+
parameters.find {|p| p.name == sym} || Parameter.new(sym)
|
25
|
+
end
|
26
|
+
|
24
27
|
end
|
25
28
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Prompt
|
2
|
+
class MultiMatcher < Matcher
|
3
|
+
|
4
|
+
def regex
|
5
|
+
"(?<#{parameter.name}>([^#{SEP}]*)(#{SEP}[^#{SEP}]*)*)"
|
6
|
+
end
|
7
|
+
|
8
|
+
def matches s
|
9
|
+
return [""] if s.length == 0
|
10
|
+
ss = StringScanner.new(s)
|
11
|
+
r = Regexp.new "[^#{SEP}]*"
|
12
|
+
res = []
|
13
|
+
until ss.eos?
|
14
|
+
ss.scan /#{SEP}/
|
15
|
+
res << ss.scan(r)
|
16
|
+
end
|
17
|
+
res
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
data/lib/prompt/parameter.rb
CHANGED
@@ -3,34 +3,26 @@ module Prompt
|
|
3
3
|
|
4
4
|
attr :name
|
5
5
|
attr :desc
|
6
|
-
attr :values
|
7
6
|
|
8
|
-
def initialize(name, desc = nil, values = nil)
|
7
|
+
def initialize(name, desc = nil, values = nil, &block)
|
9
8
|
@name = name
|
10
9
|
@desc = desc
|
11
|
-
@values = values
|
10
|
+
@values = values || []
|
11
|
+
@proc = block if block_given?
|
12
12
|
end
|
13
13
|
|
14
|
-
def
|
15
|
-
|
16
|
-
"(?<#{name}>#{values.map{|v| Regexp.escape(v)}.join("|")})"
|
17
|
-
else
|
18
|
-
"(?<#{name}>([^#{Prompt::Command::SEP}]*))"
|
19
|
-
end
|
14
|
+
def clear_cached_values
|
15
|
+
@cached_value = nil
|
20
16
|
end
|
21
17
|
|
22
|
-
def expansions
|
23
|
-
if
|
24
|
-
|
25
|
-
(v =~ /\s/) ? "\"#{v}\"" : v
|
26
|
-
end
|
18
|
+
def expansions(starting_with = "")
|
19
|
+
all = if @proc
|
20
|
+
@cached_value = @proc.call
|
27
21
|
else
|
28
|
-
|
22
|
+
@values
|
29
23
|
end
|
30
|
-
end
|
31
24
|
|
32
|
-
|
33
|
-
s
|
25
|
+
all.grep /^#{starting_with}/
|
34
26
|
end
|
35
27
|
|
36
28
|
end
|
data/lib/prompt/prompt_module.rb
CHANGED
data/spec/prompt/command_spec.rb
CHANGED
@@ -1,13 +1,12 @@
|
|
1
|
-
require 'prompt
|
2
|
-
require '
|
3
|
-
require 'prompt/glob_parameter'
|
1
|
+
require 'prompt'
|
2
|
+
require 'spec_helper'
|
4
3
|
|
5
4
|
include Prompt
|
6
5
|
|
7
6
|
describe Prompt::Command do
|
8
7
|
|
9
8
|
DIRECTIONS = %w{n e s w}
|
10
|
-
SPEEDS = %w{
|
9
|
+
SPEEDS = %w{slow slower}
|
11
10
|
|
12
11
|
describe "#match" do
|
13
12
|
|
@@ -16,13 +15,11 @@ describe Prompt::Command do
|
|
16
15
|
it "matches correctly" do
|
17
16
|
c = Command.new ["hi"]
|
18
17
|
|
19
|
-
c.match("hi").should == []
|
20
|
-
c.match("hi ").should == []
|
21
|
-
c.match(" hi").should == []
|
18
|
+
c.match(["hi"]).should == []
|
22
19
|
|
23
|
-
c.match("bye").should be_nil
|
24
|
-
c.match("h").should be_nil
|
25
|
-
c.match("").should be_nil
|
20
|
+
c.match(["bye"]).should be_nil
|
21
|
+
c.match(["h"]).should be_nil
|
22
|
+
c.match([""]).should be_nil
|
26
23
|
end
|
27
24
|
|
28
25
|
end
|
@@ -30,35 +27,24 @@ describe Prompt::Command do
|
|
30
27
|
describe "hi :name" do
|
31
28
|
|
32
29
|
it "matches correctly" do
|
33
|
-
c = Command.new ["hi",
|
30
|
+
c = Command.new ["hi", matcher(:name)]
|
34
31
|
|
35
|
-
c.match("hi guy").should == ["guy"]
|
36
|
-
c.match("hi
|
37
|
-
c.match("hi
|
38
|
-
c.match('hi "some guy"').should == ["some guy"]
|
39
|
-
c.match('hi ""').should == [""]
|
32
|
+
c.match(["hi", "guy"]).should == ["guy"]
|
33
|
+
c.match(["hi", "some guy"]).should == ["some guy"]
|
34
|
+
c.match(["hi", ""]).should == [""]
|
40
35
|
|
41
|
-
c.match("
|
42
|
-
c.match(
|
43
|
-
c.match('hi \'"').should == ['\'"']
|
44
|
-
|
45
|
-
c.match("higuy").should be_nil
|
46
|
-
c.match("higuy guy").should be_nil
|
47
|
-
|
48
|
-
c.match(" hi guy").should == ["guy"]
|
49
|
-
c.match("hi guy").should == ["guy"]
|
50
|
-
c.match("hi guy ").should == ["guy"]
|
36
|
+
c.match(["higuy"]).should be_nil
|
37
|
+
c.match(["higuy", "guy"]).should be_nil
|
51
38
|
end
|
52
39
|
|
53
40
|
it "matches correctly (with parameter value constraint)" do
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
c.match("hi
|
58
|
-
c.match("hi
|
59
|
-
c.match("hi charlie rose").should
|
60
|
-
c.match("hi zack").should
|
61
|
-
c.match("hi ali").should be_nil
|
41
|
+
name = Parameter.new(:name, "", ["alice", "bob", "charlie rose"])
|
42
|
+
c = Command.new ["hi", matcher(name)]
|
43
|
+
|
44
|
+
c.match(["hi", "alice"]).should == ["alice"]
|
45
|
+
c.match(["hi", "bob"]).should == ["bob"]
|
46
|
+
c.match(["hi", "charlie rose"]).should == ["charlie rose"]
|
47
|
+
c.match(["hi", "zack"]).should == ["zack"]
|
62
48
|
end
|
63
49
|
|
64
50
|
end
|
@@ -66,11 +52,12 @@ describe Prompt::Command do
|
|
66
52
|
describe "hi :first :last" do
|
67
53
|
|
68
54
|
it "matches correctly" do
|
69
|
-
c = Command.new ["hi",
|
55
|
+
c = Command.new ["hi", matcher(:first), matcher(:last)]
|
56
|
+
|
57
|
+
c.match(%w(hi agent smith)).should == ["agent", "smith"]
|
70
58
|
|
71
|
-
c.match(
|
72
|
-
c.match(
|
73
|
-
c.match("hi agent smith guy").should be_nil
|
59
|
+
c.match(%w(hi agent)).should be_nil
|
60
|
+
c.match(%w(hi agent smith guy)).should be_nil
|
74
61
|
end
|
75
62
|
|
76
63
|
end
|
@@ -78,14 +65,12 @@ describe Prompt::Command do
|
|
78
65
|
describe "say *stuff" do
|
79
66
|
|
80
67
|
it "matches correctly" do
|
81
|
-
c = Command.new ["say",
|
82
|
-
|
83
|
-
c.match("say hello").should == [["hello"]]
|
84
|
-
c.match("say hello world").should == [["hello", "world"]]
|
85
|
-
c.match("say
|
86
|
-
c.match("say
|
87
|
-
c.match("say 'hello world'").should == [["hello world"]]
|
88
|
-
c.match('say "hello world"').should == [["hello world"]]
|
68
|
+
c = Command.new ["say", multi_matcher(:stuff)]
|
69
|
+
|
70
|
+
c.match(["say", "hello"]).should == [["hello"]]
|
71
|
+
c.match(["say", "hello", "world"]).should == [["hello", "world"]]
|
72
|
+
c.match(["say", ""]).should == [[""]]
|
73
|
+
c.match(["say", "hello", ""]).should == [["hello", ""]]
|
89
74
|
end
|
90
75
|
|
91
76
|
end
|
@@ -93,12 +78,13 @@ describe Prompt::Command do
|
|
93
78
|
describe "say *stuff :adverb" do
|
94
79
|
|
95
80
|
it "matches correctly" do
|
96
|
-
c = Command.new ["say",
|
81
|
+
c = Command.new ["say", multi_matcher(:stuff), matcher(:adverb)]
|
82
|
+
|
83
|
+
c.match(["say", "hello", "world"]).should == [["hello"], "world"]
|
84
|
+
c.match(["say", "hello", "world", "loudly"]).should == [["hello", "world"], "loudly"]
|
85
|
+
c.match(["say", "hello", "world loudly"]).should == [["hello"], "world loudly"]
|
97
86
|
|
98
|
-
c.match("say hello").should be_nil
|
99
|
-
c.match("say hello loudly").should == [["hello"], "loudly"]
|
100
|
-
c.match("say hello world loudly").should == [["hello", "world"], "loudly"]
|
101
|
-
c.match("say hello 'world loudly'").should == [["hello"], "world loudly"]
|
87
|
+
c.match(["say", "hello"]).should be_nil
|
102
88
|
end
|
103
89
|
|
104
90
|
end
|
@@ -106,12 +92,12 @@ describe Prompt::Command do
|
|
106
92
|
describe "say *first *second" do
|
107
93
|
|
108
94
|
it "matches correctly" do
|
109
|
-
c = Command.new ["say",
|
95
|
+
c = Command.new ["say", multi_matcher(:first), multi_matcher(:second)]
|
110
96
|
|
111
|
-
c.match("say one").should be_nil
|
112
|
-
c.match("say one two").should == [["one"], ["two"]]
|
113
|
-
c.match("say one two three").should == [["one", "two"], ["three"]]
|
114
|
-
c.match("say one
|
97
|
+
c.match(["say", "one"]).should be_nil
|
98
|
+
c.match(["say", "one", "two"]).should == [["one"], ["two"]]
|
99
|
+
c.match(["say", "one", "two", "three"]).should == [["one", "two"], ["three"]]
|
100
|
+
c.match(["say", "one", "two three"]).should == [["one"], ["two three"]]
|
115
101
|
end
|
116
102
|
|
117
103
|
end
|
@@ -121,37 +107,58 @@ describe Prompt::Command do
|
|
121
107
|
describe "#parameters" do
|
122
108
|
it "returns correctly" do
|
123
109
|
color = Parameter.new(:color, "")
|
124
|
-
flavor =
|
110
|
+
flavor = Parameter.new(:flavor, "")
|
125
111
|
|
126
112
|
Command.new(["one"]).parameters.should == []
|
127
|
-
Command.new(["one", color]).parameters.should == [color]
|
128
|
-
Command.new(["one", color, flavor]).parameters == [color, flavor]
|
113
|
+
Command.new(["one", matcher(color)]).parameters.should == [color]
|
114
|
+
Command.new(["one", matcher(color), matcher(flavor)]).parameters == [color, flavor]
|
129
115
|
end
|
130
116
|
end
|
131
117
|
|
132
118
|
describe "#expansions" do
|
119
|
+
|
133
120
|
it "expands correctly with no parameters" do
|
134
|
-
Command.new
|
121
|
+
c = Command.new ["one"]
|
122
|
+
c.expansions(0, "").should == ["one"]
|
123
|
+
c.expansions(0, "on").should == ["one"]
|
124
|
+
c.expansions(0, "z").should == []
|
125
|
+
c.expansions(1, "").should == []
|
126
|
+
|
127
|
+
c = Command.new ["help", "-v"]
|
128
|
+
c.expansions(0, "").should == ["help"]
|
129
|
+
c.expansions(0, "he").should == ["help"]
|
130
|
+
c.expansions(0, "z").should == []
|
131
|
+
c.expansions(1, "").should == ["-v"]
|
132
|
+
c.expansions(1, "-v").should == ["-v"]
|
133
|
+
c.expansions(1, "z").should == []
|
135
134
|
end
|
136
135
|
|
137
136
|
it "expands correctly with undefined parameters" do
|
138
|
-
Command.new(["go",
|
137
|
+
c = Command.new(["go", matcher(:dir)])
|
138
|
+
c.expansions(0, "").should == ["go"]
|
139
|
+
c.expansions(0, "g").should == ["go"]
|
140
|
+
c.expansions(0, "go").should == ["go"]
|
141
|
+
c.expansions(1, "").should == []
|
139
142
|
end
|
140
143
|
|
141
144
|
it "expands correctly with defined parameters" do
|
142
145
|
dir = Parameter.new(:dir, "", DIRECTIONS)
|
146
|
+
c = Command.new(["go", matcher(dir)])
|
147
|
+
c.expansions(1, "").should == DIRECTIONS
|
148
|
+
|
143
149
|
speed = Parameter.new(:speed, "", SPEEDS)
|
144
|
-
Command.new(["go", dir
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
"go s quickly", "go s slowly",
|
149
|
-
"go w quickly", "go w slowly"]
|
150
|
+
c = Command.new(["go", matcher(dir), matcher(speed)])
|
151
|
+
c.expansions(2, "").should == SPEEDS
|
152
|
+
c.expansions(2, "slow").should == SPEEDS
|
153
|
+
c.expansions(2, "slowe").should == ["slower"]
|
150
154
|
end
|
151
155
|
|
152
|
-
it"expands correctly if parameter values have spaces" do
|
156
|
+
it "expands correctly if parameter values have spaces" do
|
153
157
|
speed = Parameter.new(:speed, "", ["fast", "very fast"])
|
154
|
-
Command.new(["go", speed])
|
158
|
+
c = Command.new(["go", matcher(speed)])
|
159
|
+
c.expansions(1, "").should == ['fast', 'very fast']
|
160
|
+
c.expansions(1, "f").should == ['fast']
|
161
|
+
c.expansions(1, "v").should == ['very fast']
|
155
162
|
end
|
156
163
|
end
|
157
164
|
|
@@ -160,8 +167,8 @@ describe Prompt::Command do
|
|
160
167
|
color = Parameter.new(:color, "")
|
161
168
|
|
162
169
|
Command.new(["one"]).usage.should == "one"
|
163
|
-
Command.new(["one", color]).usage.should == "one <color>"
|
164
|
-
Command.new(["one", color, "three"]).usage.should == "one <color> three"
|
170
|
+
Command.new(["one", matcher(color)]).usage.should == "one <color>"
|
171
|
+
Command.new(["one", matcher(color), "three"]).usage.should == "one <color> three"
|
165
172
|
end
|
166
173
|
end
|
167
174
|
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'prompt'
|
2
|
+
|
3
|
+
include Prompt
|
4
|
+
|
5
|
+
describe Prompt::Console do
|
6
|
+
describe ".split" do
|
7
|
+
|
8
|
+
context "simple words" do
|
9
|
+
{
|
10
|
+
"hi" => %w(hi),
|
11
|
+
"one two" => %w(one two),
|
12
|
+
"one :two" => %w(one :two),
|
13
|
+
"one *two" => %w(one *two),
|
14
|
+
"one_two" => %w(one_two)
|
15
|
+
}.each do |line, words|
|
16
|
+
it line do
|
17
|
+
Console.split(line).should == words
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
context "extra whitespace" do
|
23
|
+
{
|
24
|
+
" hi" => %w(hi),
|
25
|
+
"hi " => %w(hi),
|
26
|
+
" hi " => %w(hi),
|
27
|
+
"one two" => %w(one two),
|
28
|
+
}.each do |line, words|
|
29
|
+
it line do
|
30
|
+
Console.split(line).should == words
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context "quoted strings" do
|
36
|
+
{
|
37
|
+
"say 'hello'" => %w(say hello),
|
38
|
+
'say "hello"' => %w(say hello),
|
39
|
+
"say 'hi world'" => ["say", "hi world"],
|
40
|
+
'say "hi world"' => ["say", "hi world"],
|
41
|
+
'say ""' => ["say", ""],
|
42
|
+
"say ''" => ["say", ""],
|
43
|
+
"say '' ok" => ["say", "", "ok"],
|
44
|
+
"say 'hi world'" => ["say", "hi world"]
|
45
|
+
}.each do |line, words|
|
46
|
+
it line do
|
47
|
+
Console.split(line).should == words
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context "unmatched quotes" do
|
53
|
+
{
|
54
|
+
"'" => %w('),
|
55
|
+
'"' => %w("),
|
56
|
+
"alice's" => %w(alice's),
|
57
|
+
'alice"s' => %w(alice"s),
|
58
|
+
"'one' 'two" => %w(one 'two)
|
59
|
+
}.each do |line, words|
|
60
|
+
it line do
|
61
|
+
Console.split(line).should == words
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context "quoted substrings" do
|
67
|
+
{
|
68
|
+
"'one'two" => %w(onetwo),
|
69
|
+
"'one''two'" => %w(onetwo),
|
70
|
+
"'one'\"two\"" => %w(onetwo)
|
71
|
+
}.each do |line, words|
|
72
|
+
it line do
|
73
|
+
Console.split(line).should == words
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'prompt/matcher'
|
2
|
+
|
3
|
+
def matcher(symbol_or_parameter)
|
4
|
+
SimpleMatcher.new(param(symbol_or_parameter))
|
5
|
+
end
|
6
|
+
|
7
|
+
def multi_matcher(symbol_or_parameter)
|
8
|
+
MultiMatcher.new(param(symbol_or_parameter))
|
9
|
+
end
|
10
|
+
|
11
|
+
def param(symbol_or_parameter)
|
12
|
+
if symbol_or_parameter.kind_of? Parameter
|
13
|
+
symbol_or_parameter
|
14
|
+
else
|
15
|
+
Parameter.new(symbol_or_parameter)
|
16
|
+
end
|
17
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: prompt
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-03-08 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: Prompt makes it easy to build slick command-line applications with Tab
|
15
15
|
Completion, Command History, and Built-in Help
|
@@ -30,12 +30,15 @@ files:
|
|
30
30
|
- lib/prompt/console.rb
|
31
31
|
- lib/prompt/dsl.rb
|
32
32
|
- lib/prompt/dsl_helper.rb
|
33
|
-
- lib/prompt/
|
33
|
+
- lib/prompt/matcher.rb
|
34
|
+
- lib/prompt/multi_matcher.rb
|
34
35
|
- lib/prompt/parameter.rb
|
35
|
-
- lib/prompt/proc_parameter.rb
|
36
36
|
- lib/prompt/prompt_module.rb
|
37
|
+
- lib/prompt/simple_matcher.rb
|
37
38
|
- lib/prompt.rb
|
38
39
|
- spec/prompt/command_spec.rb
|
40
|
+
- spec/prompt/console_spec.rb
|
41
|
+
- spec/spec_helper.rb
|
39
42
|
- examples/file_manager
|
40
43
|
- examples/mud
|
41
44
|
homepage: http://github.com/mudynamics/prompt
|
@@ -49,16 +52,16 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
49
52
|
requirements:
|
50
53
|
- - ! '>='
|
51
54
|
- !ruby/object:Gem::Version
|
52
|
-
version:
|
55
|
+
version: 1.9.2
|
53
56
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
54
57
|
none: false
|
55
58
|
requirements:
|
56
59
|
- - ! '>='
|
57
60
|
- !ruby/object:Gem::Version
|
58
|
-
version:
|
61
|
+
version: '0'
|
59
62
|
requirements: []
|
60
63
|
rubyforge_project:
|
61
|
-
rubygems_version: 1.8.
|
64
|
+
rubygems_version: 1.8.11
|
62
65
|
signing_key:
|
63
66
|
specification_version: 3
|
64
67
|
summary: A small framework that makes it easy to build slick command-line applications
|
@@ -1,17 +0,0 @@
|
|
1
|
-
module Prompt
|
2
|
-
class GlobParameter < Parameter
|
3
|
-
|
4
|
-
def initialize(name, desc = nil)
|
5
|
-
super(name, desc)
|
6
|
-
end
|
7
|
-
|
8
|
-
def regex
|
9
|
-
"(?<#{name}>(([^#{Prompt::Command::SEP}]*)#{Prompt::Command::SEP}?)+)"
|
10
|
-
end
|
11
|
-
|
12
|
-
def matches s
|
13
|
-
s.split(Prompt::Command::SEP)
|
14
|
-
end
|
15
|
-
|
16
|
-
end
|
17
|
-
end
|
@@ -1,18 +0,0 @@
|
|
1
|
-
module Prompt
|
2
|
-
class ProcParameter < Parameter
|
3
|
-
|
4
|
-
def initialize(name, desc, &block)
|
5
|
-
super(name, desc, nil)
|
6
|
-
@proc = block
|
7
|
-
end
|
8
|
-
|
9
|
-
def values
|
10
|
-
@cached_value ||= @proc.call
|
11
|
-
end
|
12
|
-
|
13
|
-
def clear_cached_value
|
14
|
-
@cached_value = nil
|
15
|
-
end
|
16
|
-
|
17
|
-
end
|
18
|
-
end
|