visionmedia-commander 2.5.7 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -11,19 +11,14 @@ module Commander
11
11
 
12
12
  module UI
13
13
 
14
- ##
15
- # Format used within #log.
16
-
17
- LOG_FORMAT = "%15s %s"
18
-
19
14
  ##
20
15
  # Ask the user for a password. Specify a custom
21
- # _msg_ other than 'Password: ' or override the
16
+ # +message+ other than 'Password: ' or override the
22
17
  # default +mask+ of '*'.
23
18
 
24
- def password msg = "Password: ", mask = '*'
25
- pass = ask(msg) { |q| q.echo = mask }
26
- pass = password msg, mask if pass.empty?
19
+ def password message = 'Password: ', mask = '*'
20
+ pass = ask(message) { |q| q.echo = mask }
21
+ pass = password message, mask if pass.empty?
27
22
  pass
28
23
  end
29
24
 
@@ -35,11 +30,9 @@ module Commander
35
30
  # remove path/to/old_file.rb
36
31
  # remove path/to/old_file2.rb
37
32
  #
38
- # To alter this format simply change the Commander::UI::LOG_FORMAT
39
- # constant to whatever you like.
40
33
 
41
34
  def log action, *args
42
- say LOG_FORMAT % [action, args.join(' ')]
35
+ say '%15s %s' % [action, args.join(' ')]
43
36
  end
44
37
 
45
38
  ##
@@ -48,33 +41,34 @@ module Commander
48
41
  # Terminal progress bar utility. In its most basic form
49
42
  # requires that the developer specifies when the bar should
50
43
  # be incremented. Note that a hash of tokens may be passed to
51
- # #increment, (or returned when using Kernel#progress).
44
+ # #increment, (or returned when using Object#progress).
52
45
  #
53
- # uris = %w(
54
- # http://vision-media.ca
55
- # http://yahoo.com
56
- # http://google.com
57
- # )
46
+ # uris = %w(
47
+ # http://vision-media.ca
48
+ # http://yahoo.com
49
+ # http://google.com
50
+ # )
51
+ #
52
+ # bar = Commander::UI::ProgressBar.new uris.length, options
53
+ # threads = []
54
+ # uris.each do |uri|
55
+ # threads << Thread.new do
56
+ # begin
57
+ # res = open uri
58
+ # bar.increment :uri => uri
59
+ # rescue Exception => e
60
+ # bar.increment :uri => "#{uri} failed"
61
+ # end
62
+ # end
63
+ # end
64
+ # threads.each { |t| t.join }
58
65
  #
59
- # bar = Commander::UI::ProgressBar.new uris.length, options
60
- # threads = []
61
- # uris.each do |uri|
62
- # threads << Thread.new do
63
- # begin
64
- # res = open uri
65
- # bar.increment :uri => uri
66
- # rescue Exception => e
67
- # bar.increment :uri => "#{uri} failed"
68
- # end
69
- # end
70
- # end
71
- # threads.each { |t| t.join }
66
+ # The Object method #progress is also available:
72
67
  #
73
- # The Kernel method #progress is also available:
74
- #
75
- # progress uris, :width => 10 do |uri|
76
- # res = open uri
77
- # end
68
+ # progress uris, :width => 10 do |uri|
69
+ # res = open uri
70
+ # { :uri => uri } # Can now use :uri within :format option
71
+ # end
78
72
  #
79
73
 
80
74
  class ProgressBar
@@ -171,14 +165,12 @@ module Commander
171
165
  # Output the progress bar.
172
166
 
173
167
  def show
174
- # TODO: shift steps stack instead
175
168
  unless finished?
176
169
  erase_line
177
170
  if completed?
178
- puts @complete_message.tokenize(generate_tokens) if @complete_message.is_a? String
171
+ $terminal.say @complete_message.tokenize(generate_tokens) if @complete_message.is_a? String
179
172
  else
180
- print @format.tokenize(generate_tokens)
181
- $stdout.flush
173
+ $terminal.say @format.tokenize(generate_tokens) << ' '
182
174
  end
183
175
  end
184
176
  end
@@ -211,7 +203,8 @@ module Commander
211
203
  # Erase previous terminal line.
212
204
 
213
205
  def erase_line
214
- print "\r\e[K"
206
+ # highline does not expose the output stream
207
+ $terminal.instance_variable_get('@output').print "\r\e[K"
215
208
  end
216
209
 
217
210
  ##
@@ -226,7 +219,8 @@ module Commander
226
219
  #
227
220
  # === See:
228
221
  #
229
- # * Kernel#progress
222
+ # * Object#progress
223
+ #
230
224
 
231
225
  def self.progress arr, options = {}, &block
232
226
  bar = ProgressBar.new arr.length, options
@@ -1,4 +1,4 @@
1
1
 
2
2
  module Commander
3
- VERSION = '2.5.7'
3
+ VERSION = '3.0.0'
4
4
  end
@@ -0,0 +1,128 @@
1
+
2
+ describe Commander::Command do
3
+
4
+ before :each do
5
+ mock_terminal
6
+ create_test_command
7
+ end
8
+
9
+ describe "#seperate_switches_from_description" do
10
+ it "should seperate switches and description returning both" do
11
+ switches, description = *@command.seperate_switches_from_description('-h', '--help', 'display help')
12
+ switches.should == ['-h', '--help']
13
+ description.should == 'display help'
14
+ end
15
+ end
16
+
17
+ describe "#option" do
18
+ it "should add options" do
19
+ lambda { @command.option '--recursive' }.should change(@command.options, :length).from(2).to(3)
20
+ end
21
+
22
+ it "should allow procs as option handlers" do
23
+ @command.option('--recursive') { |recursive| recursive.should be_true }
24
+ @command.run '--recursive'
25
+ end
26
+ end
27
+
28
+ describe "#options" do
29
+ it "should distinguish between switches and descriptions" do
30
+ @command.option '-t', '--trace', 'some description'
31
+ @command.options[2][:description].should == 'some description'
32
+ @command.options[2][:switches].should == ['-t', '--trace']
33
+ end
34
+ end
35
+
36
+ describe "#switch_to_sym" do
37
+ it "should return a symbol based on the switch name" do
38
+ @command.switch_to_sym('--trace').should == :trace
39
+ @command.switch_to_sym('--foo-bar').should == :foo_bar
40
+ @command.switch_to_sym('--[no-]feature"').should == :feature
41
+ @command.switch_to_sym('--[no-]feature ARG').should == :feature
42
+ @command.switch_to_sym('--file [ARG]').should == :file
43
+ @command.switch_to_sym('--colors colors').should == :colors
44
+ end
45
+ end
46
+
47
+ describe "#run" do
48
+ describe "should invoke #when_called" do
49
+ it "with arguments seperated from options" do
50
+ @command.when_called { |args, options| args.join(' ').should == 'just some args' }
51
+ @command.run '--trace', 'just', 'some', 'args'
52
+ end
53
+
54
+ it "calling the #call method by default when an object is called" do
55
+ object = mock 'Object'
56
+ object.should_receive(:call).with(an_instance_of(Array), an_instance_of(Commander::Command::Options)).once
57
+ @command.when_called object
58
+ @command.run 'foo'
59
+ end
60
+
61
+ it "calling an arbitrary method when an object is called" do
62
+ object = mock 'Object'
63
+ object.should_receive(:foo).with(an_instance_of(Array), an_instance_of(Commander::Command::Options)).once
64
+ @command.when_called object, :foo
65
+ @command.run 'foo'
66
+ end
67
+
68
+ it "should raise an error when no handler is present" do
69
+ lambda { @command.when_called }.should raise_error(ArgumentError)
70
+ end
71
+ end
72
+
73
+ describe "should populate options with" do
74
+ it "boolean values" do
75
+ @command.option '--[no-]toggle'
76
+ @command.when_called { |_, options| options.toggle.should be_true }
77
+ @command.run '--toggle'
78
+ @command.when_called { |_, options| options.toggle.should be_false }
79
+ @command.run '--no-toggle'
80
+ end
81
+
82
+ it "manditory arguments" do
83
+ @command.option '--file FILE'
84
+ @command.when_called { |_, options| options.file.should == 'foo' }
85
+ @command.run '--file', 'foo'
86
+ lambda { @command.run '--file' }.should raise_error(OptionParser::MissingArgument)
87
+ end
88
+
89
+ it "optional arguments" do
90
+ @command.option '--use-config [file] '
91
+ @command.when_called { |_, options| options.use_config.should == 'foo' }
92
+ @command.run '--use-config', 'foo'
93
+ @command.when_called { |_, options| options.use_config.should be_nil }
94
+ @command.run '--use-config'
95
+ @command.option '--interval N', Integer
96
+ @command.when_called { |_, options| options.interval.should == 5 }
97
+ @command.run '--interval', '5'
98
+ lambda { @command.run '--interval', 'invalid' }.should raise_error(OptionParser::InvalidArgument)
99
+ end
100
+
101
+ it "lists" do
102
+ @command.option '--fav COLORS', Array
103
+ @command.when_called { |_, options| options.fav.should == ['red', 'green', 'blue'] }
104
+ @command.run '--fav', 'red,green,blue'
105
+ end
106
+
107
+ it "lists with multi-word items" do
108
+ @command.option '--fav MOVIES', Array
109
+ @command.when_called { |_, options| options.fav.should == ['super\ bad', 'nightmare'] }
110
+ @command.run '--fav', 'super\ bad,nightmare'
111
+ end
112
+
113
+ it "defaults" do
114
+ @command.option '--files LIST', Array
115
+ @command.option '--interval N', Integer
116
+ @command.when_called do |_, options|
117
+ options.default \
118
+ :files => ['foo', 'bar'],
119
+ :interval => 5
120
+ options.files.should == ['foo', 'bar']
121
+ options.interval.should == 15
122
+ end
123
+ @command.run '--interval', '15'
124
+ end
125
+ end
126
+ end
127
+
128
+ end
@@ -0,0 +1,20 @@
1
+
2
+ describe Array do
3
+
4
+ describe "#delete_switches" do
5
+ it "should delete switches such as -h or --help, returning a new array" do
6
+ ['foo', '-h', 'bar', '--help'].delete_switches.should == ['foo', 'bar']
7
+ end
8
+ end
9
+
10
+ describe "#parse" do
11
+ it "should seperate a list of words into an array" do
12
+ Array.parse('just a test').should == ['just', 'a', 'test']
13
+ end
14
+
15
+ it "should preserve escaped whitespace" do
16
+ Array.parse('just a\ test').should == ['just', 'a test']
17
+ end
18
+ end
19
+
20
+ end
@@ -1,13 +1,20 @@
1
- describe Kernel do
1
+
2
+ describe Object do
2
3
 
4
+ describe "#get_binding" do
5
+ it "should return the objects binding" do
6
+ lambda {}.get_binding.should be_instance_of(Binding)
7
+ end
8
+ end
9
+
3
10
  describe "#method_missing" do
4
11
  it "should preserve its original behavior for missing methods" do
5
12
  lambda { i_am_a_missing_method() }.should raise_error(NoMethodError)
6
13
  end
7
-
14
+
8
15
  it "should preserve its original behavior for missing variables" do
9
16
  lambda { i_am_a_missing_variable }.should raise_error(NameError)
10
17
  end
11
18
  end
12
-
19
+
13
20
  end
@@ -2,30 +2,30 @@
2
2
  describe Commander::HelpFormatter do
3
3
 
4
4
  before :each do
5
- @input = StringIO.new
6
- @output = StringIO.new
7
- $terminal = HighLine.new @input, @output
5
+ mock_terminal
6
+ end
7
+
8
+ def run *args
9
+ new_command_runner *args do
10
+ program :help_formatter, Commander::HelpFormatter::Base
11
+ end.run!
12
+ @output.string
8
13
  end
9
14
 
10
15
  it "should display global help using --help switch" do
11
- new_command_runner '--help'
12
- program :help_formatter, Commander::HelpFormatter::Base
13
- command_runner.run!
14
- @output.string.should eql("Implement global help here\n")
16
+ run('--help').should == "Implement global help here\n"
15
17
  end
16
18
 
17
19
  it "should display global help using help command" do
18
- new_command_runner 'help'
19
- program :help_formatter, Commander::HelpFormatter::Base
20
- command_runner.run!
21
- @output.string.should eql("Implement global help here\n")
20
+ run('help').should == "Implement global help here\n"
22
21
  end
23
22
 
24
23
  it "should display command help" do
25
- new_command_runner 'help', 'test'
26
- program :help_formatter, Commander::HelpFormatter::Base
27
- command_runner.run!
28
- @output.string.should eql("Implement help for test here\n")
24
+ run('help', 'test').should == "Implement help for test here\n"
25
+ end
26
+
27
+ it "should display command help using --help switch" do
28
+ run('--help', 'test').should == "Implement help for test here\n"
29
29
  end
30
30
 
31
31
  end
@@ -0,0 +1,168 @@
1
+
2
+ describe Commander do
3
+
4
+ before :each do
5
+ mock_terminal
6
+ create_test_command
7
+ end
8
+
9
+ describe "#program" do
10
+ it "should set / get program information" do
11
+ program :name, 'test'
12
+ program(:name).should == 'test'
13
+ end
14
+
15
+ it "should allow arbitrary blocks of global help documentation" do
16
+ program :help, 'Copyright', 'TJ Holowaychuk'
17
+ program(:help)['Copyright'].should == 'TJ Holowaychuk'
18
+ end
19
+
20
+ it "should raise an error when required info has not been set" do
21
+ new_command_runner '--help'
22
+ program :name, ''
23
+ lambda { run! }.should raise_error(Commander::Runner::CommandError)
24
+ end
25
+ end
26
+
27
+ describe "#command" do
28
+ it "should return a command instance when only the name is passed" do
29
+ command(:test).should be_instance_of(Commander::Command)
30
+ end
31
+
32
+ it "should raise InvalidCommandError when the command does not exist" do
33
+ lambda { command(:im_not_real) }.should raise_error(Commander::Runner::InvalidCommandError)
34
+ end
35
+ end
36
+
37
+ describe "--version" do
38
+ it "should output program version" do
39
+ new_command_runner '--version' do
40
+ program :help_formatter, Commander::HelpFormatter::Base
41
+ end.run!
42
+ @output.string.should == "test 1.2.3\n"
43
+ end
44
+ end
45
+
46
+ describe "--help" do
47
+ it "should not output an invalid command message" do
48
+ new_command_runner('--help').run!
49
+ @output.string.should_not == "invalid command. Use --help for more information\n"
50
+ end
51
+ end
52
+
53
+ describe "with invalid options" do
54
+ it "should output an invalid option message" do
55
+ new_command_runner('test', '--invalid-option').run!
56
+ @output.string.should == "invalid option: --invalid-option\n"
57
+ end
58
+ end
59
+
60
+ describe "with invalid sub-command passed" do
61
+ it "should output an invalid command message" do
62
+ new_command_runner('foo').run!
63
+ @output.string.should == "invalid command. Use --help for more information\n"
64
+ end
65
+ end
66
+
67
+ describe "with invalid sub-command passed to help" do
68
+ it "should output an invalid command message" do
69
+ new_command_runner('help', 'does_not_exist').run!
70
+ @output.string.should == "invalid command. Use --help for more information\n"
71
+ end
72
+ end
73
+
74
+ describe "#valid_command_names_from" do
75
+ it "should return array of valid command names" do
76
+ command('foo bar') {}
77
+ command('foo bar foo') {}
78
+ command_runner.valid_command_names_from('foo', 'bar', 'foo').should == ['foo bar', 'foo bar foo']
79
+ end
80
+
81
+ it "should return empty array when no possible command names exist" do
82
+ command_runner.valid_command_names_from('fake', 'command', 'name').should == []
83
+ end
84
+ end
85
+
86
+ describe "#command_name_from_args" do
87
+ it "should locate command within arbitrary arguments passed" do
88
+ new_command_runner '--help', '--arbitrary', 'test'
89
+ command_runner.command_name_from_args.should == 'test'
90
+ end
91
+
92
+ it "should support multi-word commands" do
93
+ new_command_runner '--help', '--arbitrary', 'some', 'long', 'command', 'foo'
94
+ command('some long command') {}
95
+ command_runner.command_name_from_args.should == 'some long command'
96
+ end
97
+
98
+ it "should match the longest possible command" do
99
+ new_command_runner '--help', '--arbitrary', 'foo', 'bar', 'foo'
100
+ command('foo bar') {}
101
+ command('foo bar foo') {}
102
+ command_runner.command_name_from_args.should == 'foo bar foo'
103
+ end
104
+ end
105
+
106
+ describe "#active_command" do
107
+ it "should resolve the active command" do
108
+ new_command_runner '--help', 'test'
109
+ command_runner.active_command.should be_instance_of(Commander::Command)
110
+ end
111
+
112
+ it "should resolve active command when invalid options are passed" do
113
+ new_command_runner '--help', 'test', '--arbitrary'
114
+ command_runner.active_command.should be_instance_of(Commander::Command)
115
+ end
116
+
117
+ it "should raise invalid command error when the command is not found" do
118
+ new_command_runner 'foo'
119
+ lambda { command_runner.active_command }.should raise_error(Commander::Runner::InvalidCommandError)
120
+ end
121
+ end
122
+
123
+ describe "should function correctly" do
124
+ it "when options are passed before the command name" do
125
+ new_command_runner '--trace', 'test', 'foo', 'bar' do
126
+ @command.when_called do |args, options|
127
+ args.should == ['foo', 'bar']
128
+ options.trace.should be_true
129
+ end
130
+ end.run!
131
+ end
132
+
133
+ it "when options are passed after the command name" do
134
+ new_command_runner 'test', '--trace', 'foo', 'bar' do
135
+ @command.when_called do |args, options|
136
+ args.should == ['foo', 'bar']
137
+ options.trace.should be_true
138
+ end
139
+ end.run!
140
+ end
141
+
142
+ it "when an argument passed is the same name as the command" do
143
+ new_command_runner 'test', '--trace', 'foo', 'test', 'bar' do
144
+ @command.when_called do |args, options|
145
+ args.should == ['foo', 'test', 'bar']
146
+ options.trace.should be_true
147
+ end
148
+ end.run!
149
+ end
150
+
151
+ it "when using multi-word commands" do
152
+ new_command_runner '--trace', 'my', 'command', 'something', 'foo', 'bar' do
153
+ command('my command') {}
154
+ command_runner.command_name_from_args.should == 'my command'
155
+ command_runner.args_without_command_name.should == ['--trace', 'something', 'foo', 'bar']
156
+ end.run!
157
+ end
158
+
159
+ it "when using multi-word commands with parts of the command name as arguments" do
160
+ new_command_runner '--trace', 'my', 'command', 'something', 'my', 'command' do
161
+ command('my command') {}
162
+ command_runner.command_name_from_args.should == 'my command'
163
+ command_runner.args_without_command_name.should == ['--trace', 'something', 'my', 'command']
164
+ end.run!
165
+ end
166
+ end
167
+
168
+ end