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