prompt 0.0.3 → 0.1.0

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.
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