commander 4.2.1 → 4.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|