commander 4.2.1 → 4.3.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.
- checksums.yaml +4 -4
- data/.rspec +2 -0
- data/.rubocop.yml +10 -0
- data/.rubocop_todo.yml +212 -0
- data/.travis.yml +0 -2
- data/Gemfile +1 -1
- data/History.rdoc +6 -0
- data/README.md +9 -4
- data/Rakefile +7 -4
- data/bin/commander +12 -12
- data/commander.gemspec +17 -18
- data/lib/commander/blank.rb +2 -2
- data/lib/commander/command.rb +63 -65
- data/lib/commander/configure.rb +1 -2
- data/lib/commander/core_ext.rb +1 -1
- data/lib/commander/core_ext/array.rb +2 -3
- data/lib/commander/core_ext/object.rb +1 -3
- data/lib/commander/delegates.rb +15 -4
- data/lib/commander/help_formatters.rb +2 -1
- data/lib/commander/help_formatters/base.rb +14 -7
- data/lib/commander/help_formatters/terminal.rb +5 -5
- data/lib/commander/help_formatters/terminal_compact.rb +2 -2
- data/lib/commander/methods.rb +0 -1
- data/lib/commander/runner.rb +1 -1
- data/lib/commander/user_interaction.rb +87 -91
- data/lib/commander/version.rb +1 -1
- data/spec/command_spec.rb +57 -58
- data/spec/configure_spec.rb +4 -4
- data/spec/core_ext/array_spec.rb +5 -7
- data/spec/core_ext/object_spec.rb +6 -8
- data/spec/help_formatters/terminal_spec.rb +19 -20
- data/spec/runner_spec.rb +179 -181
- data/spec/spec_helper.rb +12 -12
- data/spec/ui_spec.rb +10 -11
- metadata +23 -6
data/lib/commander/command.rb
CHANGED
@@ -3,45 +3,44 @@ require 'optparse'
|
|
3
3
|
|
4
4
|
module Commander
|
5
5
|
class Command
|
6
|
-
|
7
6
|
attr_accessor :name, :examples, :syntax, :description
|
8
7
|
attr_accessor :summary, :proxy_options, :options
|
9
|
-
|
8
|
+
|
10
9
|
##
|
11
10
|
# Options struct.
|
12
11
|
|
13
12
|
class Options
|
14
13
|
include Blank
|
15
|
-
|
14
|
+
|
16
15
|
def initialize
|
17
16
|
@table = {}
|
18
17
|
end
|
19
|
-
|
18
|
+
|
20
19
|
def __hash__
|
21
20
|
@table
|
22
21
|
end
|
23
|
-
|
24
|
-
def method_missing
|
22
|
+
|
23
|
+
def method_missing(meth, *args)
|
25
24
|
meth.to_s =~ /=$/ ? @table[meth.to_s.chop.to_sym] = args.first : @table[meth]
|
26
25
|
end
|
27
|
-
|
28
|
-
def default
|
26
|
+
|
27
|
+
def default(defaults = {})
|
29
28
|
@table = defaults.merge! @table
|
30
29
|
end
|
31
|
-
|
30
|
+
|
32
31
|
def inspect
|
33
|
-
"<Commander::Command::Options #{ __hash__.map { |k,v| "#{k}=#{v.inspect}" }.join(', ') }>"
|
32
|
+
"<Commander::Command::Options #{ __hash__.map { |k, v| "#{k}=#{v.inspect}" }.join(', ') }>"
|
34
33
|
end
|
35
34
|
end
|
36
|
-
|
35
|
+
|
37
36
|
##
|
38
37
|
# Initialize new command with specified _name_.
|
39
|
-
|
40
|
-
def initialize
|
38
|
+
|
39
|
+
def initialize(name)
|
41
40
|
@name, @examples, @when_called = name.to_s, [], []
|
42
41
|
@options, @proxy_options = [], []
|
43
42
|
end
|
44
|
-
|
43
|
+
|
45
44
|
##
|
46
45
|
# Add a usage example for this command.
|
47
46
|
#
|
@@ -49,22 +48,22 @@ module Commander
|
|
49
48
|
# created by the help formatters.
|
50
49
|
#
|
51
50
|
# === Examples
|
52
|
-
#
|
51
|
+
#
|
53
52
|
# command :something do |c|
|
54
53
|
# c.example "Should do something", "my_command something"
|
55
54
|
# end
|
56
55
|
#
|
57
|
-
|
58
|
-
def example
|
56
|
+
|
57
|
+
def example(description, command)
|
59
58
|
@examples << [description, command]
|
60
59
|
end
|
61
|
-
|
60
|
+
|
62
61
|
##
|
63
62
|
# Add an option.
|
64
63
|
#
|
65
64
|
# Options are parsed via OptionParser so view it
|
66
65
|
# for additional usage documentation. A block may optionally be
|
67
|
-
# passed to handle the option, otherwise the _options_ struct seen below
|
66
|
+
# passed to handle the option, otherwise the _options_ struct seen below
|
68
67
|
# contains the results of this option. This handles common formats such as:
|
69
68
|
#
|
70
69
|
# -h, --help options.help # => bool
|
@@ -75,18 +74,18 @@ module Commander
|
|
75
74
|
# --date [DATE] options.date # => date or nil when optional argument not set
|
76
75
|
#
|
77
76
|
# === Examples
|
78
|
-
#
|
77
|
+
#
|
79
78
|
# command :something do |c|
|
80
79
|
# c.option '--recursive', 'Do something recursively'
|
81
80
|
# c.option '--file FILE', 'Specify a file'
|
82
81
|
# c.option('--info', 'Display info') { puts "handle with block" }
|
83
82
|
# c.option '--[no-]feature', 'With or without feature'
|
84
83
|
# c.option '--list FILES', Array, 'List the files specified'
|
85
|
-
#
|
84
|
+
#
|
86
85
|
# c.when_called do |args, options|
|
87
86
|
# do_something_recursively if options.recursive
|
88
87
|
# do_something_with_file options.file if options.file
|
89
|
-
# end
|
88
|
+
# end
|
90
89
|
# end
|
91
90
|
#
|
92
91
|
# === Help Formatters
|
@@ -105,88 +104,88 @@ module Commander
|
|
105
104
|
# c.option '--time TIME', Time
|
106
105
|
# c.option '--date [DATE]', Date
|
107
106
|
#
|
108
|
-
|
109
|
-
def option
|
107
|
+
|
108
|
+
def option(*args, &block)
|
110
109
|
switches, description = Runner.separate_switches_from_description(*args)
|
111
110
|
proc = block || option_proc(switches)
|
112
111
|
@options << {
|
113
|
-
:
|
114
|
-
:
|
115
|
-
:
|
116
|
-
:
|
112
|
+
args: args,
|
113
|
+
proc: proc,
|
114
|
+
switches: switches,
|
115
|
+
description: description
|
117
116
|
}
|
118
117
|
end
|
119
|
-
|
118
|
+
|
120
119
|
##
|
121
|
-
# Handle execution of command. The handler may be a class,
|
120
|
+
# Handle execution of command. The handler may be a class,
|
122
121
|
# object, or block (see examples below).
|
123
122
|
#
|
124
123
|
# === Examples
|
125
|
-
#
|
124
|
+
#
|
126
125
|
# # Simple block handling
|
127
126
|
# c.when_called do |args, options|
|
128
127
|
# # do something
|
129
|
-
# end
|
130
|
-
#
|
128
|
+
# end
|
129
|
+
#
|
131
130
|
# # Create inst of Something and pass args / options
|
132
131
|
# c.when_called MyLib::Command::Something
|
133
|
-
#
|
132
|
+
#
|
134
133
|
# # Create inst of Something and use arbitrary method
|
135
134
|
# c.when_called MyLib::Command::Something, :some_method
|
136
|
-
#
|
135
|
+
#
|
137
136
|
# # Pass an object to handle callback (requires method symbol)
|
138
137
|
# c.when_called SomeObject, :some_method
|
139
138
|
#
|
140
|
-
|
141
|
-
def when_called
|
142
|
-
|
139
|
+
|
140
|
+
def when_called(*args, &block)
|
141
|
+
fail ArgumentError, 'must pass an object, class, or block.' if args.empty? && !block
|
143
142
|
@when_called = block ? [block] : args
|
144
143
|
end
|
145
|
-
|
146
|
-
|
144
|
+
alias_method :action, :when_called
|
145
|
+
|
147
146
|
##
|
148
147
|
# Run the command with _args_.
|
149
148
|
#
|
150
149
|
# * parses options, call option blocks
|
151
150
|
# * invokes when_called proc
|
152
151
|
#
|
153
|
-
|
154
|
-
def run
|
152
|
+
|
153
|
+
def run(*args)
|
155
154
|
call parse_options_and_call_procs(*args)
|
156
155
|
end
|
157
|
-
|
156
|
+
|
158
157
|
#:stopdoc:
|
159
|
-
|
158
|
+
|
160
159
|
##
|
161
|
-
# Parses options and calls associated procs,
|
160
|
+
# Parses options and calls associated procs,
|
162
161
|
# returning the arguments remaining.
|
163
|
-
|
164
|
-
def parse_options_and_call_procs
|
162
|
+
|
163
|
+
def parse_options_and_call_procs(*args)
|
165
164
|
return args if args.empty?
|
166
|
-
@options.inject OptionParser.new do |opts, option|
|
165
|
+
@options.inject OptionParser.new do |opts, option|
|
167
166
|
opts.on(*option[:args], &option[:proc])
|
168
167
|
opts
|
169
168
|
end.parse! args
|
170
169
|
end
|
171
|
-
|
170
|
+
|
172
171
|
##
|
173
172
|
# Call the commands when_called block with _args_.
|
174
|
-
|
175
|
-
def call
|
173
|
+
|
174
|
+
def call(args = [])
|
176
175
|
object = @when_called.shift
|
177
176
|
meth = @when_called.shift || :call
|
178
177
|
options = proxy_option_struct
|
179
178
|
case object
|
180
|
-
when Proc
|
181
|
-
when Class
|
182
|
-
else
|
183
|
-
end
|
179
|
+
when Proc then object.call(args, options)
|
180
|
+
when Class then meth != :call ? object.new.send(meth, args, options) : object.new(args, options)
|
181
|
+
else object.send(meth, args, options) if object
|
182
|
+
end
|
184
183
|
end
|
185
|
-
|
184
|
+
|
186
185
|
##
|
187
186
|
# Creates an Options instance populated with the option values
|
188
187
|
# collected by the #option_proc.
|
189
|
-
|
188
|
+
|
190
189
|
def proxy_option_struct
|
191
190
|
proxy_options.inject Options.new do |options, (option, value)|
|
192
191
|
# options that are present will evaluate to true
|
@@ -195,19 +194,18 @@ module Commander
|
|
195
194
|
options
|
196
195
|
end
|
197
196
|
end
|
198
|
-
|
197
|
+
|
199
198
|
##
|
200
199
|
# Option proxy proc used when a block is not explicitly passed
|
201
200
|
# via the #option method. This allows commander to auto-populate
|
202
201
|
# and work with option values.
|
203
|
-
|
204
|
-
def option_proc
|
205
|
-
lambda { |value| proxy_options << [Runner.switch_to_sym(switches.last), value] }
|
202
|
+
|
203
|
+
def option_proc(switches)
|
204
|
+
lambda { |value| proxy_options << [Runner.switch_to_sym(switches.last), value] }
|
206
205
|
end
|
207
|
-
|
208
|
-
def inspect
|
206
|
+
|
207
|
+
def inspect
|
209
208
|
"<Commander::Command:#{name}>"
|
210
209
|
end
|
211
|
-
|
212
210
|
end
|
213
|
-
end
|
211
|
+
end
|
data/lib/commander/configure.rb
CHANGED
@@ -5,7 +5,7 @@ module Commander
|
|
5
5
|
def configure(*configuration_opts, &configuration_block)
|
6
6
|
configuration_module = Module.new
|
7
7
|
configuration_module.extend Commander::Methods
|
8
|
-
|
8
|
+
|
9
9
|
configuration_module.class_exec(*configuration_opts, &configuration_block)
|
10
10
|
|
11
11
|
configuration_module.class_exec do
|
@@ -15,4 +15,3 @@ module Commander
|
|
15
15
|
|
16
16
|
module_function :configure
|
17
17
|
end
|
18
|
-
|
data/lib/commander/core_ext.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
|
2
2
|
class Array
|
3
|
-
|
4
3
|
##
|
5
4
|
# Split _string_ into an array. Used in
|
6
5
|
# conjunction with Highline's #ask, or #ask_for_array
|
@@ -18,9 +17,9 @@ class Array
|
|
18
17
|
# list = ask_for_array 'Favorite cookies: '
|
19
18
|
#
|
20
19
|
|
21
|
-
def self.parse
|
20
|
+
def self.parse(string)
|
22
21
|
# Using reverse + lookahead to work around Ruby 1.8's lack of lookbehind
|
22
|
+
# TODO: simplify now that we don't support Ruby 1.8
|
23
23
|
string.reverse.split(/\s(?!\\)/).reverse.map { |s| s.reverse.gsub('\\ ', ' ') }
|
24
24
|
end
|
25
|
-
|
26
25
|
end
|
data/lib/commander/delegates.rb
CHANGED
@@ -1,14 +1,25 @@
|
|
1
|
-
|
2
1
|
module Commander
|
3
2
|
module Delegates
|
4
|
-
%w(
|
5
|
-
|
6
|
-
|
3
|
+
%w(
|
4
|
+
add_command
|
5
|
+
command
|
6
|
+
program
|
7
|
+
run!
|
8
|
+
global_option
|
9
|
+
alias_command
|
10
|
+
default_command
|
11
|
+
always_trace!
|
12
|
+
never_trace!
|
13
|
+
).each do |meth|
|
7
14
|
eval <<-END, binding, __FILE__, __LINE__
|
8
15
|
def #{meth} *args, &block
|
9
16
|
::Commander::Runner.instance.#{meth} *args, &block
|
10
17
|
end
|
11
18
|
END
|
12
19
|
end
|
20
|
+
|
21
|
+
def defined_commands(*args, &block)
|
22
|
+
::Commander::Runner.instance.commands(*args, &block)
|
23
|
+
end
|
13
24
|
end
|
14
25
|
end
|
@@ -1,18 +1,25 @@
|
|
1
1
|
|
2
2
|
module Commander
|
3
|
-
|
4
3
|
##
|
5
4
|
# = Help Formatter
|
6
5
|
#
|
7
6
|
# Commander's help formatters control the output when
|
8
7
|
# either the help command, or --help switch are called.
|
9
8
|
# The default formatter is Commander::HelpFormatter::Terminal.
|
10
|
-
|
9
|
+
|
11
10
|
module HelpFormatter
|
12
|
-
class Base
|
13
|
-
def initialize
|
14
|
-
|
15
|
-
|
11
|
+
class Base
|
12
|
+
def initialize(runner)
|
13
|
+
@runner = runner
|
14
|
+
end
|
15
|
+
|
16
|
+
def render
|
17
|
+
'Implement global help here'
|
18
|
+
end
|
19
|
+
|
20
|
+
def render_command(command)
|
21
|
+
"Implement help for #{command.name} here"
|
22
|
+
end
|
16
23
|
end
|
17
24
|
end
|
18
|
-
end
|
25
|
+
end
|
@@ -7,14 +7,14 @@ module Commander
|
|
7
7
|
def render
|
8
8
|
template(:help).result @runner.get_binding
|
9
9
|
end
|
10
|
-
|
11
|
-
def render_command
|
10
|
+
|
11
|
+
def render_command(command)
|
12
12
|
template(:command_help).result command.get_binding
|
13
13
|
end
|
14
|
-
|
15
|
-
def template
|
14
|
+
|
15
|
+
def template(name)
|
16
16
|
ERB.new(File.read(File.join(File.dirname(__FILE__), 'terminal', "#{name}.erb")), nil, '-')
|
17
17
|
end
|
18
18
|
end
|
19
19
|
end
|
20
|
-
end
|
20
|
+
end
|
@@ -4,9 +4,9 @@ require 'erb'
|
|
4
4
|
module Commander
|
5
5
|
module HelpFormatter
|
6
6
|
class TerminalCompact < Terminal
|
7
|
-
def template
|
7
|
+
def template(name)
|
8
8
|
ERB.new(File.read(File.join(File.dirname(__FILE__), 'terminal_compact', "#{name}.erb")), nil, '-')
|
9
9
|
end
|
10
10
|
end
|
11
11
|
end
|
12
|
-
end
|
12
|
+
end
|
data/lib/commander/methods.rb
CHANGED
data/lib/commander/runner.rb
CHANGED
@@ -57,7 +57,7 @@ module Commander
|
|
57
57
|
return
|
58
58
|
end
|
59
59
|
global_option('-v', '--version', 'Display version information') { say version; return }
|
60
|
-
global_option('-t', '--trace', 'Display backtrace when an error occurs') { trace = true } unless @never_trace
|
60
|
+
global_option('-t', '--trace', 'Display backtrace when an error occurs') { trace = true } unless @never_trace || @always_trace
|
61
61
|
parse_global_options
|
62
62
|
remove_global_options options, @args
|
63
63
|
unless trace
|
@@ -2,22 +2,20 @@ require 'tempfile'
|
|
2
2
|
require 'shellwords'
|
3
3
|
|
4
4
|
module Commander
|
5
|
-
|
6
5
|
##
|
7
6
|
# = User Interaction
|
8
7
|
#
|
9
8
|
# Commander's user interaction module mixes in common
|
10
|
-
# methods which extend HighLine's functionality such
|
9
|
+
# methods which extend HighLine's functionality such
|
11
10
|
# as a #password method rather than calling #ask directly.
|
12
|
-
|
11
|
+
|
13
12
|
module UI
|
14
|
-
|
15
13
|
module_function
|
16
|
-
|
14
|
+
|
17
15
|
#--
|
18
16
|
# Auto include growl when available.
|
19
17
|
#++
|
20
|
-
|
18
|
+
|
21
19
|
begin
|
22
20
|
require 'growl'
|
23
21
|
rescue LoadError
|
@@ -25,26 +23,26 @@ module Commander
|
|
25
23
|
else
|
26
24
|
include Growl
|
27
25
|
end
|
28
|
-
|
26
|
+
|
29
27
|
##
|
30
28
|
# Ask the user for a password. Specify a custom
|
31
|
-
# _message_ other than 'Password: ' or override the
|
29
|
+
# _message_ other than 'Password: ' or override the
|
32
30
|
# default _mask_ of '*'.
|
33
|
-
|
34
|
-
def password
|
31
|
+
|
32
|
+
def password(message = 'Password: ', mask = '*')
|
35
33
|
pass = ask(message) { |q| q.echo = mask }
|
36
34
|
pass = password message, mask if pass.nil? || pass.empty?
|
37
35
|
pass
|
38
36
|
end
|
39
|
-
|
37
|
+
|
40
38
|
##
|
41
39
|
# Choose from a set array of _choices_.
|
42
|
-
|
43
|
-
def choose
|
40
|
+
|
41
|
+
def choose(message = nil, *choices, &block)
|
44
42
|
say message if message
|
45
43
|
super(*choices, &block)
|
46
44
|
end
|
47
|
-
|
45
|
+
|
48
46
|
##
|
49
47
|
# 'Log' an _action_ to the terminal. This is typically used
|
50
48
|
# for verbose output regarding actions performed. For example:
|
@@ -53,8 +51,8 @@ module Commander
|
|
53
51
|
# remove path/to/old_file.rb
|
54
52
|
# remove path/to/old_file2.rb
|
55
53
|
#
|
56
|
-
|
57
|
-
def log
|
54
|
+
|
55
|
+
def log(action, *args)
|
58
56
|
say '%15s %s' % [action, args.join(' ')]
|
59
57
|
end
|
60
58
|
|
@@ -66,7 +64,7 @@ module Commander
|
|
66
64
|
# say_ok 'It is ok', 'This is ok too'
|
67
65
|
#
|
68
66
|
|
69
|
-
def say_ok
|
67
|
+
def say_ok(*args)
|
70
68
|
args.each do |arg|
|
71
69
|
say $terminal.color(arg, :green)
|
72
70
|
end
|
@@ -80,7 +78,7 @@ module Commander
|
|
80
78
|
# say_warning 'Be careful', 'Think about it'
|
81
79
|
#
|
82
80
|
|
83
|
-
def say_warning
|
81
|
+
def say_warning(*args)
|
84
82
|
args.each do |arg|
|
85
83
|
say $terminal.color(arg, :yellow)
|
86
84
|
end
|
@@ -94,7 +92,7 @@ module Commander
|
|
94
92
|
# say_error 'It is not ok', 'This is not ok too'
|
95
93
|
#
|
96
94
|
|
97
|
-
def say_error
|
95
|
+
def say_error(*args)
|
98
96
|
args.each do |arg|
|
99
97
|
say $terminal.color(arg, :red)
|
100
98
|
end
|
@@ -120,28 +118,28 @@ module Commander
|
|
120
118
|
|
121
119
|
##
|
122
120
|
# Speak _message_ using _voice_ at a speaking rate of _rate_
|
123
|
-
#
|
121
|
+
#
|
124
122
|
# Voice defaults to 'Alex', which is one of the better voices.
|
125
123
|
# Speaking rate defaults to 175 words per minute
|
126
124
|
#
|
127
125
|
# === Examples
|
128
|
-
#
|
126
|
+
#
|
129
127
|
# speak 'What is your favorite food? '
|
130
128
|
# food = ask 'favorite food?: '
|
131
|
-
# speak "Wow, I like #{food} too. We have so much in common."
|
129
|
+
# speak "Wow, I like #{food} too. We have so much in common."
|
132
130
|
# speak "I like #{food} as well!", "Victoria", 190
|
133
131
|
#
|
134
132
|
# === Notes
|
135
133
|
#
|
136
134
|
# * MacOS only
|
137
135
|
#
|
138
|
-
|
139
|
-
def speak
|
136
|
+
|
137
|
+
def speak(message, voice = :Alex, rate = 175)
|
140
138
|
Thread.new { applescript "say #{message.inspect} using #{voice.to_s.inspect} speaking rate #{rate}" }
|
141
139
|
end
|
142
|
-
|
140
|
+
|
143
141
|
##
|
144
|
-
# Converse with speech recognition.
|
142
|
+
# Converse with speech recognition.
|
145
143
|
#
|
146
144
|
# Currently a "poorman's" DSL to utilize applescript and
|
147
145
|
# the MacOS speech recognition server.
|
@@ -149,7 +147,7 @@ module Commander
|
|
149
147
|
# === Examples
|
150
148
|
#
|
151
149
|
# case converse 'What is the best food?', :cookies => 'Cookies', :unknown => 'Nothing'
|
152
|
-
# when :cookies
|
150
|
+
# when :cookies
|
153
151
|
# speak 'o.m.g. you are awesome!'
|
154
152
|
# else
|
155
153
|
# case converse 'That is lame, shall I convince you cookies are the best?', :yes => 'Ok', :no => 'No', :maybe => 'Maybe another time'
|
@@ -164,31 +162,30 @@ module Commander
|
|
164
162
|
#
|
165
163
|
# * MacOS only
|
166
164
|
#
|
167
|
-
|
168
|
-
def converse
|
169
|
-
i, commands = 0, responses.map { |
|
165
|
+
|
166
|
+
def converse(prompt, responses = {})
|
167
|
+
i, commands = 0, responses.map { |_key, value| value.inspect }.join(',')
|
170
168
|
statement = responses.inject '' do |statement, (key, value)|
|
171
|
-
statement << (((i += 1) == 1 ?
|
172
|
-
%(if response is "#{value}" then\n):
|
173
|
-
|
174
|
-
%(do shell script "echo '#{key}'"\n)
|
169
|
+
statement << (((i += 1) == 1 ?
|
170
|
+
%(if response is "#{value}" then\n) : %(else if response is "#{value}" then\n))) <<
|
171
|
+
%(do shell script "echo '#{key}'"\n)
|
175
172
|
end
|
176
173
|
applescript(%(
|
177
|
-
tell application "SpeechRecognitionServer"
|
174
|
+
tell application "SpeechRecognitionServer"
|
178
175
|
set response to listen for {#{commands}} with prompt "#{prompt}"
|
179
176
|
#{statement}
|
180
177
|
end if
|
181
178
|
end tell
|
182
179
|
)).strip.to_sym
|
183
180
|
end
|
184
|
-
|
181
|
+
|
185
182
|
##
|
186
183
|
# Execute apple _script_.
|
187
|
-
|
188
|
-
def applescript
|
184
|
+
|
185
|
+
def applescript(script)
|
189
186
|
`osascript -e "#{ script.gsub('"', '\"') }"`
|
190
187
|
end
|
191
|
-
|
188
|
+
|
192
189
|
##
|
193
190
|
# Normalize IO streams, allowing for redirection of
|
194
191
|
# +input+ and/or +output+, for example:
|
@@ -213,8 +210,8 @@ module Commander
|
|
213
210
|
# end
|
214
211
|
# end
|
215
212
|
#
|
216
|
-
|
217
|
-
def io
|
213
|
+
|
214
|
+
def io(input = nil, output = nil, &block)
|
218
215
|
$stdin = File.new(input) if input
|
219
216
|
$stdout = File.new(output, 'r+') if output
|
220
217
|
if block
|
@@ -222,10 +219,10 @@ module Commander
|
|
222
219
|
reset_io
|
223
220
|
end
|
224
221
|
end
|
225
|
-
|
222
|
+
|
226
223
|
##
|
227
224
|
# Reset IO to initial constant streams.
|
228
|
-
|
225
|
+
|
229
226
|
def reset_io
|
230
227
|
$stdin, $stdout = STDIN, STDOUT
|
231
228
|
end
|
@@ -234,12 +231,12 @@ module Commander
|
|
234
231
|
# Find an editor available in path. Optionally supply the _preferred_
|
235
232
|
# editor. Returns the name as a string, nil if none is available.
|
236
233
|
|
237
|
-
def available_editor
|
238
|
-
[preferred, ENV['EDITOR'], 'mate -w', 'vim', 'vi', 'emacs', 'nano', 'pico']
|
239
|
-
compact
|
240
|
-
find {|name| system("hash #{name.split.first} 2>&-") }
|
234
|
+
def available_editor(preferred = nil)
|
235
|
+
[preferred, ENV['EDITOR'], 'mate -w', 'vim', 'vi', 'emacs', 'nano', 'pico']
|
236
|
+
.compact
|
237
|
+
.find { |name| system("hash #{name.split.first} 2>&-") }
|
241
238
|
end
|
242
|
-
|
239
|
+
|
243
240
|
##
|
244
241
|
# Prompt an editor for input. Optionally supply initial
|
245
242
|
# _input_ which is written to the editor.
|
@@ -252,8 +249,8 @@ module Commander
|
|
252
249
|
# ask_editor('foo') # => prompts EDITOR with default text of 'foo'
|
253
250
|
# ask_editor('foo', 'mate -w') # => prompts TextMate with default text of 'foo'
|
254
251
|
#
|
255
|
-
|
256
|
-
def ask_editor
|
252
|
+
|
253
|
+
def ask_editor(input = nil, preferred_editor = nil)
|
257
254
|
editor = available_editor preferred_editor
|
258
255
|
program = Commander::Runner.instance.program(:name).downcase rescue 'commander'
|
259
256
|
tmpfile = Tempfile.new program
|
@@ -265,10 +262,10 @@ module Commander
|
|
265
262
|
tmpfile.unlink
|
266
263
|
end
|
267
264
|
end
|
268
|
-
|
265
|
+
|
269
266
|
##
|
270
267
|
# Enable paging of output after called.
|
271
|
-
|
268
|
+
|
272
269
|
def enable_paging
|
273
270
|
return unless $stdout.tty?
|
274
271
|
return unless Process.respond_to? :fork
|
@@ -279,7 +276,7 @@ module Commander
|
|
279
276
|
# configurations that don't support it, but versions before 1.9 don't
|
280
277
|
# seem to do this reliably and instead raise a NotImplementedError
|
281
278
|
# (which is rescued below).
|
282
|
-
|
279
|
+
|
283
280
|
if Kernel.fork
|
284
281
|
$stdin.reopen read
|
285
282
|
write.close; read.close
|
@@ -310,21 +307,22 @@ module Commander
|
|
310
307
|
# end
|
311
308
|
#
|
312
309
|
|
313
|
-
def progress
|
310
|
+
def progress(arr, options = {})
|
314
311
|
bar = ProgressBar.new arr.length, options
|
315
312
|
bar.show
|
316
313
|
arr.each { |v| bar.increment yield(v) }
|
317
314
|
end
|
318
|
-
|
315
|
+
|
319
316
|
##
|
320
317
|
# Implements ask_for_CLASS methods.
|
321
|
-
|
318
|
+
|
322
319
|
module AskForClass
|
323
320
|
# All special cases in HighLine::Question#convert, except those that implement #parse
|
324
321
|
([Float, Integer, String, Symbol, Regexp, Array, File, Pathname] +
|
325
322
|
# All Classes that respond to #parse
|
326
323
|
Object.constants.map do |const|
|
327
324
|
# const_get(:Config) issues a deprecation warning on ruby 1.8.7
|
325
|
+
# TODO: clean up now that we're not supporting Ruby 1.8
|
328
326
|
Object.const_get(const) unless const == :Config
|
329
327
|
end.select do |const|
|
330
328
|
const.class == Class && const.respond_to?(:parse)
|
@@ -334,16 +332,16 @@ module Commander
|
|
334
332
|
end
|
335
333
|
end
|
336
334
|
end
|
337
|
-
|
335
|
+
|
338
336
|
##
|
339
337
|
# Substitute _hash_'s keys with their associated values in _str_.
|
340
|
-
|
341
|
-
def replace_tokens
|
338
|
+
|
339
|
+
def replace_tokens(str, hash) #:nodoc:
|
342
340
|
hash.inject str do |str, (key, value)|
|
343
341
|
str.gsub ":#{key}", value.to_s
|
344
342
|
end
|
345
343
|
end
|
346
|
-
|
344
|
+
|
347
345
|
##
|
348
346
|
# = Progress Bar
|
349
347
|
#
|
@@ -352,12 +350,12 @@ module Commander
|
|
352
350
|
# be incremented. Note that a hash of tokens may be passed to
|
353
351
|
# #increment, (or returned when using Object#progress).
|
354
352
|
#
|
355
|
-
# uris = %w(
|
353
|
+
# uris = %w(
|
356
354
|
# http://vision-media.ca
|
357
355
|
# http://yahoo.com
|
358
356
|
# http://google.com
|
359
357
|
# )
|
360
|
-
#
|
358
|
+
#
|
361
359
|
# bar = Commander::UI::ProgressBar.new uris.length, options
|
362
360
|
# threads = []
|
363
361
|
# uris.each do |uri|
|
@@ -381,12 +379,11 @@ module Commander
|
|
381
379
|
#
|
382
380
|
|
383
381
|
class ProgressBar
|
384
|
-
|
385
382
|
##
|
386
383
|
# Creates a new progress bar.
|
387
384
|
#
|
388
385
|
# === Options
|
389
|
-
#
|
386
|
+
#
|
390
387
|
# :title Title, defaults to "Progress"
|
391
388
|
# :width Width of :progress_bar
|
392
389
|
# :progress_str Progress string, defaults to "="
|
@@ -397,7 +394,7 @@ module Commander
|
|
397
394
|
#
|
398
395
|
# === Tokens
|
399
396
|
#
|
400
|
-
# :title
|
397
|
+
# :title
|
401
398
|
# :percent_complete
|
402
399
|
# :progress_bar
|
403
400
|
# :step
|
@@ -407,7 +404,7 @@ module Commander
|
|
407
404
|
# :time_remaining
|
408
405
|
#
|
409
406
|
|
410
|
-
def initialize
|
407
|
+
def initialize(total, options = {})
|
411
408
|
@total_steps, @step, @start_time = total, 0, Time.now
|
412
409
|
@title = options.fetch :title, 'Progress'
|
413
410
|
@width = options.fetch :width, 25
|
@@ -417,10 +414,10 @@ module Commander
|
|
417
414
|
@format = options.fetch :format, ':title |:progress_bar| :percent_complete% complete '
|
418
415
|
@tokens = options.fetch :tokens, {}
|
419
416
|
end
|
420
|
-
|
417
|
+
|
421
418
|
##
|
422
419
|
# Completion percentage.
|
423
|
-
|
420
|
+
|
424
421
|
def percent_complete
|
425
422
|
if @total_steps.zero?
|
426
423
|
100
|
@@ -428,50 +425,49 @@ module Commander
|
|
428
425
|
@step * 100 / @total_steps
|
429
426
|
end
|
430
427
|
end
|
431
|
-
|
428
|
+
|
432
429
|
##
|
433
430
|
# Time that has elapsed since the operation started.
|
434
|
-
|
431
|
+
|
435
432
|
def time_elapsed
|
436
433
|
Time.now - @start_time
|
437
434
|
end
|
438
|
-
|
435
|
+
|
439
436
|
##
|
440
437
|
# Estimated time remaining.
|
441
|
-
|
438
|
+
|
442
439
|
def time_remaining
|
443
440
|
(time_elapsed / @step) * steps_remaining
|
444
441
|
end
|
445
|
-
|
442
|
+
|
446
443
|
##
|
447
444
|
# Number of steps left.
|
448
|
-
|
445
|
+
|
449
446
|
def steps_remaining
|
450
447
|
@total_steps - @step
|
451
448
|
end
|
452
|
-
|
449
|
+
|
453
450
|
##
|
454
451
|
# Formatted progress bar.
|
455
|
-
|
452
|
+
|
456
453
|
def progress_bar
|
457
454
|
(@progress_str * (@width * percent_complete / 100)).ljust @width, @incomplete_str
|
458
455
|
end
|
459
|
-
|
456
|
+
|
460
457
|
##
|
461
458
|
# Generates tokens for this step.
|
462
|
-
|
459
|
+
|
463
460
|
def generate_tokens
|
464
461
|
{
|
465
|
-
:
|
466
|
-
:
|
467
|
-
:
|
468
|
-
:
|
469
|
-
:
|
470
|
-
:
|
471
|
-
:
|
472
|
-
:
|
473
|
-
}.
|
474
|
-
merge! @tokens
|
462
|
+
title: @title,
|
463
|
+
percent_complete: percent_complete,
|
464
|
+
progress_bar: progress_bar,
|
465
|
+
step: @step,
|
466
|
+
steps_remaining: steps_remaining,
|
467
|
+
total_steps: @total_steps,
|
468
|
+
time_elapsed: '%0.2fs' % time_elapsed,
|
469
|
+
time_remaining: @step > 0 ? '%0.2fs' % time_remaining : ''
|
470
|
+
}.merge! @tokens
|
475
471
|
end
|
476
472
|
|
477
473
|
##
|
@@ -487,10 +483,10 @@ module Commander
|
|
487
483
|
end
|
488
484
|
end
|
489
485
|
end
|
490
|
-
|
486
|
+
|
491
487
|
##
|
492
488
|
# Whether or not the operation is complete, and we have finished.
|
493
|
-
|
489
|
+
|
494
490
|
def finished?
|
495
491
|
@step == @total_steps + 1
|
496
492
|
end
|
@@ -506,7 +502,7 @@ module Commander
|
|
506
502
|
# Increment progress. Optionally pass _tokens_ which
|
507
503
|
# can be displayed in the output format.
|
508
504
|
|
509
|
-
def increment
|
505
|
+
def increment(tokens = {})
|
510
506
|
@step += 1
|
511
507
|
@tokens.merge! tokens if tokens.is_a? Hash
|
512
508
|
show
|