visionmedia-commander 2.5.7 → 3.0.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.
@@ -1,5 +1,4 @@
1
1
 
2
- require 'commander/core_ext/kernel'
3
2
  require 'commander/core_ext/array'
4
3
  require 'commander/core_ext/object'
5
4
  require 'commander/core_ext/string'
@@ -2,23 +2,28 @@
2
2
  class Array
3
3
 
4
4
  ##
5
- # Split +string+ into an array.
5
+ # Split +string+ into an array. Used in
6
+ # conjunection with Highline's ask, or ask_for_array
7
+ # methods, which must respond to #parse.
6
8
  #
7
9
  # === Highline example:
8
10
  #
9
11
  # # ask invokes Array#parse
10
12
  # list = ask 'Favorite cookies:', Array
11
13
  #
14
+ # # or use ask_for_CLASS
15
+ # list = ask_for_array 'Favorite cookies: '
16
+ #
12
17
 
13
18
  def self.parse string
14
- string.split
19
+ eval "%w(#{string})"
15
20
  end
16
21
 
17
22
  ##
18
- # Delete switches such as -h or --help.
23
+ # Delete switches such as -h or --help. Mutative.
19
24
 
20
25
  def delete_switches
21
- self.dup.delete_if { |v| v.to_s =~ /^-/ }
26
+ self.delete_if { |value| value.to_s =~ /^-/ }
22
27
  end
23
28
 
24
29
  end
@@ -1,13 +1,48 @@
1
1
 
2
+ require 'forwardable'
3
+
4
+ ##
5
+ # Delegates the following methods:
6
+ #
7
+ # * Commander::Runner#add_command
8
+ # * Commander::Runner#command
9
+ # * Commander::Runner#commands
10
+ # * Commander::Runner#program
11
+ # * Commander::UI::ProgressBar#progress
12
+ #
13
+
2
14
  class Object
3
15
 
16
+ extend Forwardable
4
17
  include Commander::UI
5
18
 
19
+ def_delegators :$command_runner, :add_command, :command, :program, :run!, :commands
20
+ def_delegators Commander::UI::ProgressBar, :progress
21
+
6
22
  ##
7
- # Used with ERB in Commander::HelpFormatter::Base.
23
+ # Return the current binding.
8
24
 
9
25
  def get_binding
10
26
  binding
11
27
  end
28
+
29
+ ##
30
+ # Return the current command runner.
12
31
 
13
- end
32
+ def command_runner
33
+ $command_runner
34
+ end
35
+
36
+ ##
37
+ # Implement #ask_for_CLASS.
38
+
39
+ include Module.new {
40
+ def method_missing meth, *args, &block
41
+ case meth.to_s
42
+ when /^ask_for_([\w]+)/ ; $terminal.ask(args.first, eval($1.capitalize))
43
+ else super
44
+ end
45
+ end
46
+ }
47
+
48
+ end
@@ -2,10 +2,10 @@
2
2
  class String
3
3
 
4
4
  ##
5
- # Replace +hash+ keys with associated values. Mutative.
5
+ # Replace +hash+ keys with associated values.
6
6
 
7
7
  def tokenize! hash = {}
8
- hash.each { |k, v| gsub! /:#{k}/, v.to_s }
8
+ hash.each { |key, value| gsub! /:#{key}/, value.to_s }
9
9
  self
10
10
  end
11
11
 
@@ -16,16 +16,4 @@ class String
16
16
  self.dup.tokenize! hash
17
17
  end
18
18
 
19
- ##
20
- # Converts a string to camelcase.
21
-
22
- def camelcase upcase_first_letter = true
23
- up = upcase_first_letter
24
- s = dup
25
- s.gsub!(/\/(.?)/){ "::#{$1.upcase}" }
26
- s.gsub!(/(?:_+|-+)([a-z])/){ $1.upcase }
27
- s.gsub!(/(\A|\s)([a-z])/){ $1 + $2.upcase } if up
28
- s
29
- end
30
-
31
19
  end
@@ -1,6 +1,8 @@
1
1
 
2
2
  require 'commander/help_formatters/base'
3
3
 
4
- module Commander::HelpFormatter
5
- autoload :Terminal, 'commander/help_formatters/terminal'
4
+ module Commander
5
+ module HelpFormatter
6
+ autoload :Terminal, 'commander/help_formatters/terminal'
7
+ end
6
8
  end
@@ -9,30 +9,10 @@ module Commander
9
9
  # The default formatter is Commander::HelpFormatter::Terminal.
10
10
 
11
11
  module HelpFormatter
12
-
13
12
  class Base
14
-
15
- ##
16
- # Access the current command runner via +@runner+.
17
-
18
- def initialize runner
19
- @runner = runner
20
- end
21
-
22
- ##
23
- # Renders global help.
24
-
25
- def render
26
- "Implement global help here"
27
- end
28
-
29
- ##
30
- # Renders +command+ specific help.
31
-
32
- def render_command command
33
- "Implement help for #{command.name} here"
34
- end
35
-
13
+ def initialize runner; @runner = runner end
14
+ def render; 'Implement global help here' end
15
+ def render_command command; "Implement help for #{command.name} here" end
36
16
  end
37
17
  end
38
18
  end
@@ -3,13 +3,6 @@ require 'erb'
3
3
 
4
4
  module Commander
5
5
  module HelpFormatter
6
-
7
- ##
8
- # = Terminal
9
- #
10
- # Outputs help in a terminal friendly format,
11
- # utilizing asni formatting via the highline gem.
12
-
13
6
  class Terminal < Base
14
7
  def render
15
8
  template(:help).result @runner.get_binding
@@ -20,7 +13,7 @@ module Commander
20
13
  end
21
14
 
22
15
  def template name
23
- ERB.new(File.read(File.expand_path(File.dirname(__FILE__)) + "/terminal/#{name}.erb"), nil, "-")
16
+ ERB.new(File.read(File.expand_path(File.dirname(__FILE__)) + "/terminal/#{name}.erb"), nil, '-')
24
17
  end
25
18
  end
26
19
  end
@@ -1,31 +1,31 @@
1
1
 
2
- <%= color "NAME", :bold %>:
2
+ <%= $terminal.color "NAME", :bold %>:
3
3
 
4
4
  <%= @name %>
5
5
 
6
- <%= color "DESCRIPTION", :bold %>:
6
+ <%= $terminal.color "DESCRIPTION", :bold %>:
7
7
 
8
8
  <%= @description || @summary || 'No description.' -%>
9
9
 
10
10
  <% if @syntax -%>
11
11
 
12
- <%= color "SYNOPSIS", :bold %>:
12
+ <%= $terminal.color "SYNOPSIS", :bold %>:
13
13
 
14
14
  <%= @syntax -%>
15
15
 
16
16
  <% end -%>
17
17
  <% unless @examples.empty? -%>
18
18
 
19
- <%= color "EXAMPLES", :bold %>:
20
- <% @examples.each do |example| -%>
19
+ <%= $terminal.color "EXAMPLES", :bold %>:
20
+ <% @examples.each do |description, command| -%>
21
21
 
22
- # <%= example[:description] %>
23
- <%= example[:command] %>
22
+ # <%= description %>
23
+ <%= command %>
24
24
  <% end -%>
25
25
  <% end -%>
26
26
  <% unless @options.empty? -%>
27
27
 
28
- <%= color "OPTIONS", :bold %>:
28
+ <%= $terminal.color "OPTIONS", :bold %>:
29
29
  <% @options.each do |option| -%>
30
30
 
31
31
  <%= option[:switches].join ', ' %>
@@ -1,19 +1,19 @@
1
1
 
2
- <%= color "NAME", :bold %>:
2
+ <%= $terminal.color "NAME", :bold %>:
3
3
 
4
4
  <%= program :name %>
5
5
 
6
- <%= color "DESCRIPTION", :bold %>:
6
+ <%= $terminal.color "DESCRIPTION", :bold %>:
7
7
 
8
8
  <%= program :description %>
9
9
 
10
- <%= color "SUB-COMMANDS", :bold %>:
10
+ <%= $terminal.color "SUB-COMMANDS", :bold %>:
11
11
  <% @commands.each_pair do |name, command| %>
12
12
  <%= "%-20s %s" % [command.name, command.summary || command.description] -%>
13
13
  <% end %>
14
14
  <% if program :help -%>
15
15
  <% program(:help).each_pair do |title, body| %>
16
- <%= color title.to_s.upcase, :bold %>:
16
+ <%= $terminal.color title.to_s.upcase, :bold %>:
17
17
 
18
18
  <%= body %>
19
19
  <% end %>
@@ -22,31 +22,30 @@ module Commander
22
22
  attr_reader :options
23
23
 
24
24
  ##
25
- # Initialize a new command runner.
25
+ # Initialize a new command runner. Optionally
26
+ # supplying +args+ for mocking, or arbitrary usage.
26
27
 
27
28
  def initialize args = ARGV
28
- @args = args
29
- @commands, @options, @program = {}, {}, {
30
- :help_formatter => Commander::HelpFormatter::Terminal,
31
- :int_message => "\nProcess interrupted",
32
- }
29
+ @args, @commands, @options = args, {}, {}
30
+ @program = program_defaults
33
31
  create_default_commands
34
32
  parse_global_options
35
33
  end
36
34
 
37
35
  ##
38
- # Run the command parsing and execution process immediately.
36
+ # Run command parsing and execution process.
39
37
 
40
38
  def run!
41
- %w[ name version description ].each { |k| ensure_program_key_set k.to_sym }
39
+ require_program :name, :version, :description
42
40
  case
43
- when options[:version] : $terminal.say "#{@program[:name]} #{@program[:version]}"
44
- when options[:help] : get_command(:help).run
45
- else active_command.run args_without_command
41
+ when options[:version] ; $terminal.say "#{program(:name)} #{program(:version)}"
42
+ when options[:help] ; command(:help).run(*@args[1..@args.length])
43
+ else active_command.run *args_without_command_name
46
44
  end
47
45
  rescue InvalidCommandError
48
- $terminal.say "invalid command. Use --help for more information"
49
- rescue OptionParser::InvalidOption,
46
+ $terminal.say 'invalid command. Use --help for more information'
47
+ rescue \
48
+ OptionParser::InvalidOption,
50
49
  OptionParser::InvalidArgument,
51
50
  OptionParser::MissingArgument => e
52
51
  $terminal.say e
@@ -57,25 +56,25 @@ module Commander
57
56
  #
58
57
  # === Examples:
59
58
  #
60
- # # Set data
61
- # program :name, 'Commander'
62
- # program :version, Commander::VERSION
63
- # program :description, 'Commander utility program.'
64
- # program :help, 'Copyright', '2008 TJ Holowaychuk'
65
- # program :help, 'Anything', 'You want'
66
- # program :int_message 'Bye bye!'
67
- #
68
- # # Get data
69
- # program :name # => 'Commander'
59
+ # # Set data
60
+ # program :name, 'Commander'
61
+ # program :version, Commander::VERSION
62
+ # program :description, 'Commander utility program.'
63
+ # program :help, 'Copyright', '2008 TJ Holowaychuk'
64
+ # program :help, 'Anything', 'You want'
65
+ # program :int_message 'Bye bye!'
66
+ #
67
+ # # Get data
68
+ # program :name # => 'Commander'
70
69
  #
71
70
  # === Keys:
72
71
  #
73
- # :name (required) Program name
74
- # :version (required) Program version triple, ex: '0.0.1'
75
- # :description (required) Program description
76
- # :help_formatter Defaults to Commander::HelpFormatter::Terminal
77
- # :help Allows addition of arbitrary global help blocks
78
- # :int_message Message to display when interrupted (CTRL + C)
72
+ # :name (required) Program name
73
+ # :version (required) Program version triple, ex: '0.0.1'
74
+ # :description (required) Program description
75
+ # :help_formatter Defaults to Commander::HelpFormatter::Terminal
76
+ # :help Allows addition of arbitrary global help blocks
77
+ # :int_message Message to display when interrupted (CTRL + C)
79
78
  #
80
79
 
81
80
  def program key, *args
@@ -84,31 +83,30 @@ module Commander
84
83
  @program[:help][args.first] = args[1]
85
84
  else
86
85
  @program[key] = *args unless args.empty?
87
- @program[key] if args.empty?
86
+ @program[key]
88
87
  end
89
88
  end
90
89
 
91
90
  ##
92
- # Generate a command object instance using a block
93
- # evaluated with the command as its scope.
91
+ # Creates and yields a command instance when a block is passed.
92
+ # Otherise attempts to return the command, raising InvalidCommandError when
93
+ # it does not exist.
94
94
  #
95
95
  # === Examples:
96
96
  #
97
- # command :my_command do |c|
98
- # c.when_called do |args|
99
- # # Code
100
- # end
101
- # end
102
- #
103
- # === See:
104
- #
105
- # * Commander::Command
106
- # * Commander::Runner#add_command
97
+ # command :my_command do |c|
98
+ # c.when_called do |args|
99
+ # # Code
100
+ # end
101
+ # end
107
102
  #
108
103
 
109
104
  def command name, &block
110
- command = Commander::Command.new(name) and yield command
111
- add_command command
105
+ if block
106
+ yield add_command(Commander::Command.new(name))
107
+ else
108
+ @commands[name.to_s] or raise InvalidCommandError, "invalid command '#{ name || 'nil' }'", caller
109
+ end
112
110
  end
113
111
 
114
112
  ##
@@ -119,14 +117,7 @@ module Commander
119
117
  end
120
118
 
121
119
  ##
122
- # Get a command object if available or nil.
123
-
124
- def get_command name
125
- @commands[name.to_s] or raise InvalidCommandError, "invalid command '#{ name || 'nil' }'", caller
126
- end
127
-
128
- ##
129
- # Check if a command exists.
120
+ # Check if a command +name+ exists.
130
121
 
131
122
  def command_exists? name
132
123
  @commands[name.to_s]
@@ -134,60 +125,70 @@ module Commander
134
125
 
135
126
  ##
136
127
  # Get active command within arguments passed to this runner.
137
- #
138
- # === See:
139
- #
140
- # * Commander::Runner#parse_global_options
141
- #
142
128
 
143
129
  def active_command
144
- @_active_command ||= get_command(command_name_from_args)
130
+ @__active_command ||= command(command_name_from_args)
145
131
  end
146
132
 
147
133
  ##
148
- # Attemps to locate command from @args. Supports multi-word
149
- # command names.
134
+ # Attemps to locate a command name from within the arguments.
135
+ # Supports multi-word commands, using the largest possible match.
150
136
 
151
137
  def command_name_from_args
152
- @_command_name_from_args ||= @args.delete_switches.inject do |name, arg|
153
- return name if command_exists? name
154
- name += " #{arg}"
155
- end
138
+ @__command_name_from_args ||= valid_command_names_from(*@args.dup).last
139
+ end
140
+
141
+ ##
142
+ # Returns array of valid command names found within +args+.
143
+
144
+ def valid_command_names_from *args
145
+ arg_string = args.delete_switches.join ' '
146
+ commands.keys.map { |key| key if arg_string.include? key }.compact
156
147
  end
157
148
 
158
149
  ##
159
150
  # Help formatter instance.
160
151
 
161
152
  def help_formatter
162
- @_help_formatter ||= @program[:help_formatter].new self
153
+ @__help_formatter ||= program(:help_formatter).new self
163
154
  end
164
155
 
165
156
  ##
166
157
  # Return arguments without the command name.
167
158
 
168
- def args_without_command
169
- @args.dup - command_name_from_args.split
159
+ def args_without_command_name
160
+ removed = []
161
+ parts = command_name_from_args.split
162
+ @args.dup.delete_if do |arg|
163
+ removed << arg if parts.include?(arg) and not removed.include?(arg)
164
+ end
170
165
  end
171
166
 
172
167
  private
173
168
 
169
+ ##
170
+ # Returns hash of program defaults.
171
+
172
+ def program_defaults
173
+ return :help_formatter => HelpFormatter::Terminal, :int_message => "\nProcess interrupted"
174
+ end
175
+
174
176
  ##
175
177
  # Creates default commands such as 'help' which is
176
178
  # essentially the same as using the --help switch.
177
179
 
178
180
  def create_default_commands
179
181
  command :help do |c|
180
- c.syntax = "command help <sub_command>"
181
- c.summary = "Display help documentation for <sub_command>"
182
- c.description = "Display help documentation for the global or sub commands"
183
- c.example "Display global help", "command help"
184
- c.example "Display help for 'foo'", "command help foo"
182
+ c.syntax = 'command help <sub_command>'
183
+ c.summary = 'Display help documentation for <sub_command>'
184
+ c.description = 'Display help documentation for the global or sub commands'
185
+ c.example 'Display global help', 'command help'
186
+ c.example "Display help for 'foo'", 'command help foo'
185
187
  c.when_called do |args, options|
186
- gen = help_formatter
187
188
  if args.empty?
188
- $terminal.say gen.render
189
+ $terminal.say help_formatter.render
189
190
  else
190
- $terminal.say gen.render_command(get_command(args.join(' ')))
191
+ $terminal.say help_formatter.render_command(command(args.join(' ')))
191
192
  end
192
193
  end
193
194
  end
@@ -199,14 +200,11 @@ module Commander
199
200
  # These options are used by commander itself
200
201
  # as well as allowing your program to specify
201
202
  # global commands such as '--verbose'.
202
- #
203
- # TODO: allow 'option' method for global program
204
- #
205
203
 
206
204
  def parse_global_options
207
205
  opts = OptionParser.new
208
- opts.on("--help") { @options[:help] = true }
209
- opts.on("--version") { @options[:version] = true }
206
+ opts.on('--help') { @options[:help] = true }
207
+ opts.on('--version') { @options[:version] = true }
210
208
  opts.parse! @args.dup
211
209
  rescue OptionParser::InvalidOption
212
210
  # Ignore invalid options since options will be further
@@ -214,10 +212,12 @@ module Commander
214
212
  end
215
213
 
216
214
  ##
217
- # Raises a CommandError when the program +key+ is not present, or is empty.
215
+ # Raises a CommandError when the program any of the +keys+ are not present, or empty.
218
216
 
219
- def ensure_program_key_set key
220
- raise CommandError, "Program #{key} required (use #program method)" if (@program[key].nil? || @program[key].empty?)
217
+ def require_program *keys
218
+ keys.each do |key|
219
+ raise CommandError, "program #{key} required" if program(key).nil? or program(key).empty?
220
+ end
221
221
  end
222
222
 
223
223
  end