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 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
- desc "Move"
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
- desc "Interact"
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
- go east go north go south go west
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
- desc "Taco commands"
89
+ group "File commands"
90
90
 
91
91
  command ...
92
92
  command ...
93
93
 
94
- desc "Burger commands"
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 "say *sentence" do |sentence|
116
- puts "You said #{sentence.length} words"
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 valid values for the parameter.
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 a static list of valid values
128
+ ### Specifying parameter completions
129
129
 
130
- You can specify a static list of valid values for a parameter. These will be expanded when using tab completion.
130
+ You can specify the completions for a parameter as a static list:
131
131
 
132
132
  ```ruby
133
- param :name, "Description", %w(value1 value2)
133
+ param :color, "A color", %w(red green blue)
134
134
  ```
135
135
 
136
- ### Specifying a dynamic list of valid values
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
@@ -8,7 +8,7 @@ def change_prompt dir
8
8
  Prompt.application.prompt = "#{@pwd}> "
9
9
  end
10
10
 
11
- @pwd = File.absolute_path Dir.pwd
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.absolute_path File.join(@pwd, dir)
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 :files, "A list of files"
41
+ param :file, "File" do
42
+ Dir.entries(@pwd)
43
+ end
42
44
 
43
- command "cp *files :dir", "Copy one or more files to dest" do |files, dir|
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
- desc "Move"
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
- desc "Interact"
23
+ group "Interact"
24
24
 
25
25
  command "look", "Look around" do
26
26
  if @moves < GRUE
@@ -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 use_command_group desc
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 command_str
25
+ def exec words
23
26
  commands.each do |command|
24
- args = command.match(command_str)
27
+ args = command.match(words)
25
28
  return command.run(args) if args
26
29
  end
27
- raise CommandNotFound.new(command_str)
30
+ raise CommandNotFound.new
28
31
  ensure
29
32
  clear_cached_values
30
33
  end
31
34
 
32
- def completions starting_with
33
- all_expansions.grep /^#{Regexp.escape(starting_with)}/
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(&:expansions).flatten(1)
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
@@ -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
- @parameters = words.select {|w| w.kind_of? Parameter}
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 str
30
- if m = regex.match(to_args(str).join(SEP))
31
- @parameters.map {|v| v.matches(m[v.name]) }
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 Parameter
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
- private
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
- # Splits a command string into an argument list.
59
- # This understands how to make "quoted strings" into a single arg
60
- def to_args command
61
- args = []
62
- ss = StringScanner.new(command)
63
- ss.scan(/\s+/)
64
- until ss.eos?
65
- if ss.scan(S_QUOTED_ARG) or ss.scan(D_QUOTED_ARG)
66
- args << ss[1]
67
- elsif arg = ss.scan(UNQUOTED_ARG)
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
- args
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 Parameter
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,7 +1,4 @@
1
1
  module Prompt
2
2
  class CommandNotFound < RuntimeError
3
- def initialize(command_str)
4
- super "Command not found: #{command_str}"
5
- end
6
3
  end
7
4
  end
@@ -6,7 +6,7 @@ module Prompt
6
6
  class Builtins
7
7
  extend DSL
8
8
 
9
- desc "Console commands"
9
+ group "Console commands"
10
10
 
11
11
  command "help", "List all commands" do
12
12
  print_help
@@ -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 |s|
10
- Prompt.application.completions(s)
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
- Prompt.application.exec(line).nil?
31
- rescue ::Prompt::CommandNotFound => e
32
- STDERR.puts e.message
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.use_command_group(name)
13
+ Prompt.application.select_group(name)
16
14
  end
17
15
 
18
- def desc desc
19
- Prompt.application.use_command_group(desc)
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
- @parameters << ProcParameter.new(name, desc, &block)
29
+ @parameters << if block
30
+ Parameter.new(name, desc, &block)
33
31
  else
34
- @parameters << Parameter.new(name, desc, values)
32
+ Parameter.new(name, desc, values)
35
33
  end
36
34
  end
37
35
 
@@ -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 Parameters
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
- if word[0] == ":"
8
- sym = word[1..-1].to_sym
9
- parameters.find {|p| p.name == sym} || Parameter.new(sym)
10
- elsif word[0] == "*"
11
- sym = word[1..-1].to_sym
12
- param = parameters.find {|p| p.name == sym}
13
- # Since GlobParameters can't be defined ahead of time in the DSL, we
14
- # always create a new one and copy the description
15
- # This is kinda jankey
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,13 @@
1
+ module Prompt
2
+ class Matcher
3
+
4
+ SEP = Prompt::Command::SEP
5
+
6
+ attr :parameter
7
+
8
+ def initialize param
9
+ @parameter = param
10
+ end
11
+
12
+ end
13
+ 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
@@ -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 regex
15
- if values
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 values
24
- values.map do |v|
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
- ["<#{name}>"]
22
+ @values
29
23
  end
30
- end
31
24
 
32
- def matches s
33
- s
25
+ all.grep /^#{starting_with}/
34
26
  end
35
27
 
36
28
  end
@@ -1,5 +1,5 @@
1
1
  module Prompt
2
- VERSION = "0.0.3"
2
+ VERSION = "0.1.0"
3
3
 
4
4
  class << self
5
5
 
@@ -0,0 +1,13 @@
1
+ module Prompt
2
+ class SimpleMatcher < Matcher
3
+
4
+ def regex
5
+ "(?<#{parameter.name}>[^#{Prompt::Command::SEP}]*)"
6
+ end
7
+
8
+ def matches s
9
+ s
10
+ end
11
+
12
+ end
13
+ end
@@ -1,13 +1,12 @@
1
- require 'prompt/command'
2
- require 'prompt/parameter'
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{quickly slowly}
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", Parameter.new(:name)]
30
+ c = Command.new ["hi", matcher(:name)]
34
31
 
35
- c.match("hi guy").should == ["guy"]
36
- c.match("hi 'some guy'").should == ["some guy"]
37
- c.match("hi ''").should == [""]
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("hi '").should == ["'"]
42
- c.match('hi "').should == ['"']
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
- c = Command.new ["hi", Parameter.new(:name, "", ["alice", "bob", "charlie rose"])]
55
-
56
- c.match("hi alice").should == ["alice"]
57
- c.match("hi bob").should == ["bob"]
58
- c.match("hi 'charlie rose'").should == ["charlie rose"]
59
- c.match("hi charlie rose").should be_nil
60
- c.match("hi zack").should be_nil
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", Parameter.new(:first), Parameter.new(:last)]
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("hi agent smith").should == ["agent", "smith"]
72
- c.match("hi agent").should be_nil
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", GlobParameter.new(:stuff)]
82
-
83
- c.match("say hello").should == [["hello"]]
84
- c.match("say hello world").should == [["hello", "world"]]
85
- c.match("say hello world").should == [["hello", "world"]]
86
- c.match("say hello world").should == [["hello", "world"]]
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", GlobParameter.new(:stuff), Parameter.new(:adverb)]
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", GlobParameter.new(:first), GlobParameter.new(:second)]
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 'two three'").should == [["one"], ["two three"]]
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 = GlobParameter.new(: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(["one"]).expansions.should == ["one"]
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", Parameter.new(:dir)]).expansions.should == ["go <dir>"]
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]).expansions.should == ["go n", "go e", "go s", "go w"]
145
- Command.new(["go", dir, speed]).expansions.should ==
146
- ["go n quickly", "go n slowly",
147
- "go e quickly", "go e slowly",
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]).expansions.should == ['go fast', 'go "very fast"']
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
@@ -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.3
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-02-23 00:00:00.000000000 Z
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/glob_parameter.rb
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: '0'
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: 1.3.6
61
+ version: '0'
59
62
  requirements: []
60
63
  rubyforge_project:
61
- rubygems_version: 1.8.15
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