visionmedia-commander 3.1.8 → 3.2.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/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