visionmedia-commander 3.1.8 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/History.rdoc CHANGED
@@ -1,4 +1,17 @@
1
1
 
2
+ === 3.2.0 / 2009-03-26
3
+
4
+ * Added implied small switches so they appear in help (-h, -v, etc)
5
+ * Added #inspect back to Commander::Command::Options [#1]
6
+ * Added inheritance of global options for sub-commands [#7]
7
+ * Added #require_valid_command
8
+ * Renamed #call_active_command to #run_active_command
9
+ * Changed; using same option format as sub-command options for globals [#18]
10
+ * Changed; program :name is now optional, and is auto-defined when not specified [#21]
11
+ * Moved #switch_to_sym from Command to Commander::Runner
12
+ * Moved #seperate_switches_from_description into Commander::Runner [#22]
13
+ * Removed program :name from commander init template since its not required anymore
14
+
2
15
  === 3.1.8 / 2009-03-25
3
16
 
4
17
  * Utilizing abort and $stderr instead of using #say [#16]
data/README.rdoc CHANGED
@@ -27,6 +27,7 @@ as well as an object, specifying a method to call, so view the RDoc for more inf
27
27
  require 'rubygems'
28
28
  require 'commander'
29
29
 
30
+ # :name is optional, otherwise uses the basename of this executable
30
31
  program :name, 'Foo Bar'
31
32
  program :version, '1.0.0'
32
33
  program :description, 'Stupid command that prints foo or bar.'
@@ -221,6 +222,32 @@ you can add additional global options:
221
222
 
222
223
  This method accepts the same syntax as Commander::Command#option so check it out for documentation.
223
224
 
225
+ All global options regardless of providing a block are accessable at the sub-command level. This
226
+ means that instead of the following:
227
+
228
+ global_option('--verbose') { $verbose = true }
229
+ ...
230
+ c.when_called do |args, options|
231
+ say 'foo' if $verbose
232
+ ...
233
+
234
+ You may:
235
+
236
+ global_option '--verbose'
237
+ ...
238
+ c.when_called do |args, options|
239
+ say 'foo' if options.verbose
240
+ ...
241
+
242
+ == Tips
243
+
244
+ When adding a global or sub-command option, OptionParser implicitly adds a small
245
+ switch even when not explicitly created, for example -c will be the same as --config
246
+ in both examples:
247
+
248
+ global_option '-c', '--config FILE'
249
+ global_option '--config FILE'
250
+
224
251
  == ASCII Tables
225
252
 
226
253
  For feature rich ASCII tables for your terminal app check out visionmedia's terminal-table gem at
data/bin/commander CHANGED
@@ -27,7 +27,6 @@ command :init do |c|
27
27
  require 'commander'
28
28
  require '#{name}'
29
29
 
30
- program :name, '#{name}'
31
30
  program :version, #{name.capitalize}::VERSION
32
31
  program :description, '#{description}'
33
32
 
data/commander.gemspec CHANGED
@@ -2,11 +2,11 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{commander}
5
- s.version = "3.1.8"
5
+ s.version = "3.2.0"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["TJ Holowaychuk"]
9
- s.date = %q{2009-03-25}
9
+ s.date = %q{2009-03-26}
10
10
  s.default_executable = %q{commander}
11
11
  s.description = %q{The complete solution for Ruby command-line executables}
12
12
  s.email = %q{tj@vision-media.ca}
@@ -4,8 +4,8 @@ require 'optparse'
4
4
  module Commander
5
5
  class Command
6
6
 
7
- attr_reader :name, :examples, :options, :proxy_options
8
- attr_accessor :syntax, :description, :summary
7
+ attr_accessor :name, :examples, :syntax, :description
8
+ attr_accessor :summary, :proxy_options, :options
9
9
 
10
10
  ##
11
11
  # Options struct.
@@ -28,6 +28,10 @@ module Commander
28
28
  def default defaults = {}
29
29
  @table = defaults.merge! @table
30
30
  end
31
+
32
+ def inspect
33
+ "<Commander::Command::Options #{ __hash__.map { |k,v| "#{k}=#{v.inspect}" }.join(', ') }>"
34
+ end
31
35
  end
32
36
 
33
37
  ##
@@ -103,7 +107,7 @@ module Commander
103
107
  #
104
108
 
105
109
  def option *args, &block
106
- switches, description = seperate_switches_from_description *args
110
+ switches, description = Runner.seperate_switches_from_description *args
107
111
  proc = block || option_proc(switches)
108
112
  @options << {
109
113
  :args => args,
@@ -176,36 +180,12 @@ module Commander
176
180
  end
177
181
  end
178
182
 
179
- ##
180
- # Attempts to generate a method name symbol from +switch+.
181
- # For example:
182
- #
183
- # -h # => :h
184
- # --trace # => :trace
185
- # --some-switch # => :some_switch
186
- # --[no-]feature # => :feature
187
- # --file FILE # => :file
188
- # --list of,things # => :list
189
-
190
- def switch_to_sym switch
191
- switch.scan(/[\-\]](\w+)/).join('_').to_sym rescue nil
192
- end
193
-
194
- ##
195
- # Return switches and description seperated from the +args+ passed.
196
-
197
- def seperate_switches_from_description *args
198
- switches = args.find_all { |arg| arg.to_s =~ /^-/ }
199
- description = args.last unless !args.last.is_a? String or args.last.match(/^-/)
200
- return switches, description
201
- end
202
-
203
183
  ##
204
184
  # Creates an Options instance populated with the option values
205
185
  # collected by the #option_proc.
206
186
 
207
187
  def proxy_option_struct
208
- @proxy_options.inject Options.new do |options, (option, value)|
188
+ proxy_options.inject Options.new do |options, (option, value)|
209
189
  options.__send__ :"#{option}=", value
210
190
  options
211
191
  end
@@ -217,9 +197,7 @@ module Commander
217
197
  # and work with option values.
218
198
 
219
199
  def option_proc switches
220
- Proc.new do |value|
221
- @proxy_options << [switch_to_sym(switches.last), value]
222
- end
200
+ lambda { |value| proxy_options << [Runner.switch_to_sym(switches.last), value] }
223
201
  end
224
202
 
225
203
  def inspect #:nodoc:
@@ -21,10 +21,12 @@
21
21
  <% end %>
22
22
  <% unless @options.empty? -%>
23
23
  <%= $terminal.color "GLOBAL OPTIONS", :bold %>:
24
- <% @options.each do |(args, proc)| %>
25
- <%= "%-20s %s" % [args.shift, args.last.is_a?(String) ? args.last : ''] -%>
26
- <% end -%>
27
- <% end %>
24
+ <% @options.each do |option| -%>
25
+
26
+ <%= option[:switches].join ', ' %>
27
+ <%= option[:description] %>
28
+ <% end -%>
29
+ <% end -%>
28
30
  <% if program :help -%>
29
31
  <% program(:help).each_pair do |title, body| %>
30
32
  <%= $terminal.color title.to_s.upcase, :bold %>:
@@ -36,18 +36,18 @@ module Commander
36
36
 
37
37
  def run!
38
38
  trace = false
39
- require_program :name, :version, :description
39
+ require_program :version, :description
40
40
  trap('INT') { abort program(:int_message) }
41
- global_option('--help', 'Display help documentation') { command(:help).run *@args[1..-1]; return }
42
- global_option('--version', 'Display version information') { say version; return }
43
- global_option('--trace', 'Display backtrace when an error occurs') { trace = true }
41
+ global_option('-h', '--help', 'Display help documentation') { command(:help).run *@args[1..-1]; return }
42
+ global_option('-v', '--version', 'Display version information') { say version; return }
43
+ global_option('-t', '--trace', 'Display backtrace when an error occurs') { trace = true }
44
44
  parse_global_options
45
45
  remove_global_options
46
46
  unless trace
47
47
  begin
48
- call_active_command
49
- rescue InvalidCommandError
50
- abort 'invalid command. Use --help for more information'
48
+ run_active_command
49
+ rescue InvalidCommandError => e
50
+ abort "#{e}. Use --help for more information"
51
51
  rescue \
52
52
  OptionParser::InvalidOption,
53
53
  OptionParser::InvalidArgument,
@@ -57,7 +57,7 @@ module Commander
57
57
  abort "error: #{e}. Use --trace to view backtrace"
58
58
  end
59
59
  else
60
- call_active_command
60
+ run_active_command
61
61
  end
62
62
  end
63
63
 
@@ -69,9 +69,10 @@ module Commander
69
69
  end
70
70
 
71
71
  ##
72
- # Invoke the active command.
72
+ # Run the active command.
73
73
 
74
- def call_active_command
74
+ def run_active_command
75
+ require_valid_command
75
76
  if alias? command_name_from_args
76
77
  active_command.run *(@aliases[command_name_from_args.to_s] + args_without_command_name)
77
78
  else
@@ -131,16 +132,21 @@ module Commander
131
132
 
132
133
  def command name, &block
133
134
  yield add_command(Commander::Command.new(name)) if block
134
- @commands[name.to_s] or raise InvalidCommandError, "invalid command '#{ name || 'nil' }'", caller
135
+ @commands[name.to_s]
135
136
  end
136
137
 
137
138
  ##
138
- # Add a global option; follows the same syntax as
139
- # Command#option. This would be used for switches such
140
- # as --version, --trace, etc.
139
+ # Add a global option; follows the same syntax as Command#option
140
+ # This would be used for switches such as --version, --trace, etc.
141
141
 
142
142
  def global_option *args, &block
143
- @options << [args, block]
143
+ switches, description = Runner.seperate_switches_from_description *args
144
+ @options << {
145
+ :args => args,
146
+ :proc => block,
147
+ :switches => switches,
148
+ :description => description,
149
+ }
144
150
  end
145
151
 
146
152
  ##
@@ -228,7 +234,9 @@ module Commander
228
234
  # Returns hash of program defaults.
229
235
 
230
236
  def program_defaults
231
- return :help_formatter => HelpFormatter::Terminal, :int_message => "\nProcess interrupted"
237
+ return :help_formatter => HelpFormatter::Terminal,
238
+ :int_message => "\nProcess interrupted",
239
+ :name => File.basename($0)
232
240
  end
233
241
 
234
242
  ##
@@ -246,12 +254,21 @@ module Commander
246
254
  if args.empty?
247
255
  say help_formatter.render
248
256
  else
249
- say help_formatter.render_command(command(args.join(' ')))
257
+ command = command args.join(' ')
258
+ require_valid_command command
259
+ say help_formatter.render_command(command)
250
260
  end
251
261
  end
252
262
  end
253
263
  end
254
264
 
265
+ ##
266
+ # Raises InvalidCommandError when a +command+ is not found.
267
+
268
+ def require_valid_command command = active_command
269
+ raise InvalidCommandError, 'invalid command', caller if command.nil?
270
+ end
271
+
255
272
  ##
256
273
  # Removes global options from args. This prevents an invalid
257
274
  # option error from ocurring when options are parsed
@@ -259,8 +276,8 @@ module Commander
259
276
 
260
277
  def remove_global_options
261
278
  # TODO: refactor with flipflop
262
- options.each do |(args, proc)|
263
- switch, has_arg = args.first.split
279
+ options.each do |option|
280
+ switch, has_arg = option[:args].first.split
264
281
  past_switch, arg_removed = false, false
265
282
  @args.delete_if do |arg|
266
283
  if arg == switch
@@ -280,14 +297,29 @@ module Commander
280
297
  # Parse global command options.
281
298
 
282
299
  def parse_global_options
283
- options.inject OptionParser.new do |options, (args, proc)|
284
- options.on *args, &proc
300
+ options.inject OptionParser.new do |options, option|
301
+ options.on *option[:args], &global_option_proc(option[:switches], &option[:proc])
285
302
  end.parse! @args.dup
286
303
  rescue OptionParser::InvalidOption
287
304
  # Ignore invalid options since options will be further
288
305
  # parsed by our sub commands.
289
306
  end
290
307
 
308
+ ##
309
+ # Returns a proc allowing for sub-commands to inherit global options.
310
+ # This functionality works weither a block is present for the global
311
+ # option or not, so simple switches such as --verbose can be used
312
+ # without a block, and used throughout all sub-commands.
313
+
314
+ def global_option_proc switches, &block
315
+ lambda do |value|
316
+ unless active_command.nil?
317
+ active_command.proxy_options << [Runner.switch_to_sym(switches.last), value]
318
+ end
319
+ yield value if block and !value.nil?
320
+ end
321
+ end
322
+
291
323
  ##
292
324
  # Raises a CommandError when the program any of the +keys+ are not present, or empty.
293
325
 
@@ -297,6 +329,30 @@ module Commander
297
329
  end
298
330
  end
299
331
 
332
+ ##
333
+ # Return switches and description seperated from the +args+ passed.
334
+
335
+ def self.seperate_switches_from_description *args
336
+ switches = args.find_all { |arg| arg.to_s =~ /^-/ }
337
+ description = args.last unless !args.last.is_a? String or args.last.match(/^-/)
338
+ return switches, description
339
+ end
340
+
341
+ ##
342
+ # Attempts to generate a method name symbol from +switch+.
343
+ # For example:
344
+ #
345
+ # -h # => :h
346
+ # --trace # => :trace
347
+ # --some-switch # => :some_switch
348
+ # --[no-]feature # => :feature
349
+ # --file FILE # => :file
350
+ # --list of,things # => :list
351
+
352
+ def self.switch_to_sym switch
353
+ switch.scan(/[\-\]](\w+)/).join('_').to_sym rescue nil
354
+ end
355
+
300
356
  private
301
357
 
302
358
  def say *args #:nodoc:
@@ -1,4 +1,4 @@
1
1
 
2
2
  module Commander
3
- VERSION = '3.1.8'
3
+ VERSION = '3.2.0'
4
4
  end
data/spec/command_spec.rb CHANGED
@@ -24,14 +24,6 @@ describe Commander::Command do
24
24
  end
25
25
  end
26
26
 
27
- describe "#seperate_switches_from_description" do
28
- it "should seperate switches and description returning both" do
29
- switches, description = *@command.seperate_switches_from_description('-h', '--help', 'display help')
30
- switches.should == ['-h', '--help']
31
- description.should == 'display help'
32
- end
33
- end
34
-
35
27
  describe "#option" do
36
28
  it "should add options" do
37
29
  lambda { @command.option '--recursive' }.should change(@command.options, :length).from(1).to(2)
@@ -49,17 +41,6 @@ describe Commander::Command do
49
41
  end
50
42
  end
51
43
 
52
- describe "#switch_to_sym" do
53
- it "should return a symbol based on the switch name" do
54
- @command.switch_to_sym('--trace').should == :trace
55
- @command.switch_to_sym('--foo-bar').should == :foo_bar
56
- @command.switch_to_sym('--[no-]feature"').should == :feature
57
- @command.switch_to_sym('--[no-]feature ARG').should == :feature
58
- @command.switch_to_sym('--file [ARG]').should == :file
59
- @command.switch_to_sym('--colors colors').should == :colors
60
- end
61
- end
62
-
63
44
  describe "#run" do
64
45
  describe "should invoke #when_called" do
65
46
  it "with arguments seperated from options" do
data/spec/runner_spec.rb CHANGED
@@ -20,7 +20,7 @@ describe Commander do
20
20
 
21
21
  it "should raise an error when required info has not been set" do
22
22
  new_command_runner '--help'
23
- program :name, ''
23
+ program :version, ''
24
24
  lambda { run! }.should raise_error(Commander::Runner::CommandError)
25
25
  end
26
26
  end
@@ -30,8 +30,27 @@ describe Commander do
30
30
  command(:test).should be_instance_of(Commander::Command)
31
31
  end
32
32
 
33
- it "should raise InvalidCommandError when the command does not exist" do
34
- lambda { command(:im_not_real) }.should raise_error(Commander::Runner::InvalidCommandError)
33
+ it "should return nil when the command does not exist" do
34
+ command(:im_not_real).should be_nil
35
+ end
36
+ end
37
+
38
+ describe "#seperate_switches_from_description" do
39
+ it "should seperate switches and description returning both" do
40
+ switches, description = *Commander::Runner.seperate_switches_from_description('-h', '--help', 'display help')
41
+ switches.should == ['-h', '--help']
42
+ description.should == 'display help'
43
+ end
44
+ end
45
+
46
+ describe "#switch_to_sym" do
47
+ it "should return a symbol based on the switch name" do
48
+ Commander::Runner.switch_to_sym('--trace').should == :trace
49
+ Commander::Runner.switch_to_sym('--foo-bar').should == :foo_bar
50
+ Commander::Runner.switch_to_sym('--[no-]feature"').should == :feature
51
+ Commander::Runner.switch_to_sym('--[no-]feature ARG').should == :feature
52
+ Commander::Runner.switch_to_sym('--file [ARG]').should == :file
53
+ Commander::Runner.switch_to_sym('--colors colors').should == :colors
35
54
  end
36
55
  end
37
56
 
@@ -42,14 +61,15 @@ describe Commander do
42
61
  end
43
62
 
44
63
  it "should pass arguments passed to the alias when called" do
64
+ gem_name = ''
45
65
  new_command_runner 'install', 'gem', 'commander' do
46
66
  command :install do |c|
47
67
  c.option '--gem-name NAME', 'Install a gem'
48
- c.when_called { |_, options| options.gem_name.should == 'commander' }
68
+ c.when_called { |_, options| gem_name = options.gem_name }
49
69
  end
50
70
  alias_command :'install gem', :install, '--gem-name'
51
- command(:install).should_receive(:run).once
52
71
  end.run!
72
+ gem_name.should == 'commander'
53
73
  end
54
74
  end
55
75
 
@@ -61,6 +81,28 @@ describe Commander do
61
81
  end.run!
62
82
  file.should == 'foo'
63
83
  end
84
+
85
+ it "should be inherited by sub-commands" do
86
+ quiet = nil
87
+ new_command_runner 'foo', '--quiet' do
88
+ global_option('--quiet', 'Suppress output')
89
+ command :foo do |c|
90
+ c.when_called { |_, options| quiet = options.quiet }
91
+ end
92
+ end.run!
93
+ quiet.should be_true
94
+ end
95
+
96
+ it "should be inherited by sub-commands even when a block is present" do
97
+ quiet = nil
98
+ new_command_runner 'foo', '--quiet' do
99
+ global_option('--quiet', 'Suppress output') {}
100
+ command :foo do |c|
101
+ c.when_called { |_, options| quiet = options.quiet }
102
+ end
103
+ end.run!
104
+ quiet.should be_true
105
+ end
64
106
  end
65
107
 
66
108
  describe "--trace" do
@@ -165,9 +207,9 @@ describe Commander do
165
207
  command_runner.active_command.should be_instance_of(Commander::Command)
166
208
  end
167
209
 
168
- it "should raise invalid command error when the command is not found" do
210
+ it "should return nil when the command is not found" do
169
211
  new_command_runner 'foo'
170
- lambda { command_runner.active_command }.should raise_error(Commander::Runner::InvalidCommandError)
212
+ command_runner.active_command.should be_nil
171
213
  end
172
214
  end
173
215
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: visionmedia-commander
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.8
4
+ version: 3.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - TJ Holowaychuk
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-03-25 00:00:00 -07:00
12
+ date: 2009-03-26 00:00:00 -07:00
13
13
  default_executable: commander
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency