drydock 0.3.3 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES.txt +19 -4
- data/README.rdoc +37 -28
- data/bin/example +43 -27
- data/drydock.gemspec +1 -25
- data/lib/drydock.rb +308 -119
- metadata +1 -25
- data/doc/classes/Drydock.html +0 -535
- data/doc/classes/Drydock/Command.html +0 -188
- data/doc/classes/Drydock/InvalidArgument.html +0 -118
- data/doc/classes/Drydock/MissingArgument.html +0 -88
- data/doc/classes/Drydock/NoCommandsDefined.html +0 -88
- data/doc/classes/Drydock/UnknownCommand.html +0 -118
- data/doc/created.rid +0 -1
- data/doc/files/CHANGES_txt.html +0 -123
- data/doc/files/LICENSE_txt.html +0 -87
- data/doc/files/README_rdoc.html +0 -127
- data/doc/files/bin/example.html +0 -90
- data/doc/files/lib/drydock_rb.html +0 -75
- data/doc/fr_class_index.html +0 -19
- data/doc/fr_file_index.html +0 -24
- data/doc/fr_method_index.html +0 -4457
- data/doc/index.html +0 -15
- data/doc/rdoc-style.css +0 -319
- data/test/command_test.rb +0 -40
data/CHANGES.txt
CHANGED
@@ -1,15 +1,30 @@
|
|
1
1
|
DRYDOCK, CHANGES
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
*
|
3
|
+
#### 0.4 (2009-02-28) ###############################
|
4
|
+
|
5
|
+
* FIX: Bug, "interning empty string" error when bare "option" is used
|
6
|
+
* NEW: Calls valid? method (if present) before calling command block.
|
7
|
+
* NEW: "capture" method. Auto capture STDOUT to obj.stdout etc...
|
8
|
+
* NEW: Automatically calls init and print_header methods before the command
|
9
|
+
and print_footer after the command (if available)
|
10
|
+
* NEW: Tries to call obj.command if available when no block is supplied
|
11
|
+
* NEW: "show_commands" command built-in. Displays commands with descriptions
|
12
|
+
* NEW: A default usage help msg for every command: "#{$0} command-name"
|
13
|
+
* NEW: "usage" work multiple times for the same command.
|
14
|
+
* NEW: "desc" method for per command descriptions
|
15
|
+
* CHANGE: options are now stored as obj.option.name instead of obj.name
|
16
|
+
* CHANGE: global options are now stored as obj.globals.name
|
17
|
+
* CHANGE: removed auto importing methods
|
18
|
+
OLD: require 'drydock'
|
19
|
+
NEW: require 'drydock'
|
20
|
+
extend Drydock
|
6
21
|
|
7
22
|
|
8
23
|
#### 0.3.3 (2009-02-14) ###############################
|
9
24
|
|
10
25
|
* NEW: init method hook for subclasses of Drydock::Command
|
11
26
|
* UPDATED: Rdocs
|
12
|
-
|
27
|
+
* CHANGE: added method command_aliaz to mirror aliaz_command
|
13
28
|
|
14
29
|
#### 0.3 (2009-02-05) ###############################
|
15
30
|
|
data/README.rdoc
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
= Drydock - v0.
|
1
|
+
= Drydock - v0.4
|
2
2
|
|
3
3
|
Inspired by github-gem and bmizerany-frylock.
|
4
4
|
|
@@ -31,40 +31,49 @@ See bin/example for more.
|
|
31
31
|
# variables defined here will be available to all commands.
|
32
32
|
end
|
33
33
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
34
|
+
desc "A friendly welcome to the Drydock"
|
35
|
+
command :welcome do
|
36
|
+
puts "Welcome to Drydock."
|
37
|
+
puts "For available commands:"
|
38
|
+
puts "#{$0} show-commands"
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
usage "USAGE: #{$0} laugh [-f]"
|
43
|
+
desc "The captain commands his crew to laugh"
|
44
|
+
option :f, :faster, "A boolean value. Go even faster!"
|
45
|
+
command :laugh do |obj|
|
46
|
+
# +obj+ is an instance of Drydock::Command. The options you define are available
|
47
|
+
# via obj.option.name
|
48
|
+
|
49
|
+
answer = !obj.option.faster ? "Sort of" : "Yes! I'm literally laughing as fast as possible."
|
50
|
+
|
51
|
+
puts "Captain Stubing: Are you laughing?"
|
52
|
+
puts "Dr. Bricker: " << answer
|
53
|
+
end
|
54
|
+
|
42
55
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
56
|
+
class JohnWestSmokedOysters < Drydock::Command
|
57
|
+
# You can write your own command classes by inheriting from Drydock::Command
|
58
|
+
# and referencing it in the command definition.
|
59
|
+
def ahoy!; p "matey"; end
|
60
|
+
end
|
48
61
|
|
49
|
-
|
62
|
+
desc "Do something with John West's Smoked Oysters"
|
63
|
+
command :oysters => JohnWestSmokedOysters do |obj|
|
64
|
+
p obj # => #<JohnWestSmokedOysters:0x42179c ... >
|
65
|
+
end
|
50
66
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
class JohnWestSmokedOysters < Drydock::Command; end;
|
56
|
-
# You can write your own command classes by inheriting from Drydock::Command
|
57
|
-
# and referencing it in the command definition.
|
67
|
+
desc "My way of saying hello!"
|
68
|
+
command :ahoy! => JohnWestSmokedOysters
|
69
|
+
# If you don't provide a block, Drydock will call JohnWestSmokedOysters#ahoy!
|
58
70
|
|
59
|
-
command :oysters => JohnWestSmokedOysters do |obj|
|
60
|
-
p obj # => #<JohnWestSmokedOysters:0x42179c ... >
|
61
|
-
end
|
62
71
|
|
63
72
|
== More Information
|
64
73
|
|
65
|
-
* http://github.com/delano/drydock
|
66
|
-
* http://drydock.rubyforge.org/
|
67
|
-
* http://www.youtube.com/watch?v=m_wFEB4Oxlo
|
74
|
+
* GitHub[http://github.com/delano/drydock]
|
75
|
+
* RDocs[http://drydock.rubyforge.org/]
|
76
|
+
* Inspiration[http://www.youtube.com/watch?v=m_wFEB4Oxlo]
|
68
77
|
|
69
78
|
== Credits
|
70
79
|
|
data/bin/example
CHANGED
@@ -13,8 +13,14 @@
|
|
13
13
|
$:.unshift File.expand_path(File.join(File.dirname(__FILE__), '..')), 'lib'
|
14
14
|
|
15
15
|
require 'drydock'
|
16
|
+
extend Drydock # Tell Drydock you want its methods!
|
16
17
|
|
17
|
-
|
18
|
+
|
19
|
+
default :welcome # The welcome command will be run if no command is given
|
20
|
+
capture :stderr # Drydock will capture STDERR and keep it in the hold.
|
21
|
+
# You can use this to suppress errors.
|
22
|
+
|
23
|
+
project "Drydock Example" # An optional name
|
18
24
|
|
19
25
|
before do
|
20
26
|
# You can execute a block before the requests command is executed. Instance
|
@@ -25,30 +31,29 @@ after do
|
|
25
31
|
# And this will be called after the command.
|
26
32
|
end
|
27
33
|
|
34
|
+
desc "A friendly welcome to the Drydock"
|
28
35
|
command :welcome do
|
29
|
-
|
30
|
-
|
31
|
-
puts "
|
32
|
-
|
33
|
-
# The commands method returns a hash of Drydock::Command objects
|
34
|
-
puts commands.keys.inject([]) { |list, command| list << command.to_s }.sort.join(', ')
|
36
|
+
puts "Welcome to Drydock."
|
37
|
+
puts "For available commands:"
|
38
|
+
puts "#{$0} show-commands"
|
35
39
|
end
|
36
40
|
|
37
|
-
usage "
|
41
|
+
usage "USAGE: #{$0} laugh [-f]"
|
42
|
+
desc "The captain commands his crew to laugh"
|
38
43
|
option :f, :faster, "A boolean value. Go even faster!"
|
39
44
|
command :laugh do |obj|
|
40
45
|
# +obj+ is an instance of Drydock::Command. The options you define are available
|
41
|
-
# via
|
46
|
+
# via obj.option.name
|
42
47
|
|
43
|
-
answer = !obj.faster ? "Sort of" : "Yes! I'm literally laughing as fast as possible."
|
48
|
+
answer = !obj.option.faster ? "Sort of" : "Yes! I'm literally laughing as fast as possible."
|
44
49
|
|
45
50
|
puts "Captain Stubing: Are you laughing?"
|
46
51
|
puts "Dr. Bricker: " << answer
|
47
52
|
end
|
48
53
|
|
49
54
|
global_usage "USAGE: #{File.basename($0)} [global options] command [command options]"
|
50
|
-
|
51
|
-
|
55
|
+
global :s, :seconds, "Display values in seconds"
|
56
|
+
global :v, :verbose, "Verbosity level (i.e. -vvv is greater than -v)" do |v|
|
52
57
|
# Use instance variables to maintain values between option blocks.
|
53
58
|
# This will increment for every -v found (i.e. -vvv)
|
54
59
|
@val ||= 0
|
@@ -57,17 +62,19 @@ end
|
|
57
62
|
|
58
63
|
|
59
64
|
usage "#{$0} [-s] [-vv] date"
|
65
|
+
desc "Display the current date"
|
60
66
|
command :date do |obj, argv|
|
61
67
|
# +argv+ is an array containing the unnamed arguments
|
62
68
|
require 'time'
|
63
69
|
now = Time.now
|
64
|
-
puts "(Not verbose enough. Try adding a -v.)" if (obj.verbose || 0) == 1
|
65
|
-
puts "More verbosely, the date is now: " if (obj.verbose || 0) >= 2
|
66
|
-
puts (obj.seconds) ? now.to_i : now.to_s
|
70
|
+
puts "(Not verbose enough. Try adding a -v.)" if (obj.global.verbose || 0) == 1
|
71
|
+
puts "More verbosely, the date is now: " if (obj.global.verbose || 0) >= 2
|
72
|
+
puts (obj.global.seconds) ? now.to_i : now.to_s
|
67
73
|
end
|
68
74
|
|
69
|
-
|
75
|
+
|
70
76
|
ignore :options
|
77
|
+
desc "This command ignores options"
|
71
78
|
command :rogue do |obj, argv|
|
72
79
|
# You can use ignore :options to tell Drydock to not process the
|
73
80
|
# command-specific options.
|
@@ -79,14 +86,26 @@ command :rogue do |obj, argv|
|
|
79
86
|
end
|
80
87
|
end
|
81
88
|
|
82
|
-
class JohnWestSmokedOysters < Drydock::Command
|
83
|
-
# You can write your own command classes by inheriting from Drydock::Command
|
84
|
-
# and referencing it in the command definition.
|
89
|
+
class JohnWestSmokedOysters < Drydock::Command
|
90
|
+
# You can write your own command classes by inheriting from Drydock::Command
|
91
|
+
# and referencing it in the command definition.
|
92
|
+
def ahoy!; p "matey"; end
|
93
|
+
end
|
85
94
|
|
95
|
+
desc "Do something with John West's Smoked Oysters"
|
86
96
|
command :oysters => JohnWestSmokedOysters do |obj|
|
87
97
|
p obj # => #<JohnWestSmokedOysters:0x42179c ... >
|
88
98
|
end
|
89
99
|
|
100
|
+
desc "My way of saying hello!"
|
101
|
+
command :ahoy! => JohnWestSmokedOysters
|
102
|
+
# If you don't provide a block, Drydock will call JohnWestSmokedOysters#ahoy!
|
103
|
+
|
104
|
+
|
105
|
+
|
106
|
+
usage 'ruby bin/example process -c -d " " -t 15 http://solutious.com/'
|
107
|
+
usage 'echo "http://solutious.com/" | ruby bin/example process -c -d " " -t 15'
|
108
|
+
desc "Check for broken URIs"
|
90
109
|
option :c, :check, "Check response codes for each URI"
|
91
110
|
option :d, :delim, String, "Output delimiter"
|
92
111
|
option :t, :timeout, Float, "Timeout value for HTTP request" do |v|
|
@@ -95,9 +114,7 @@ option :t, :timeout, Float, "Timeout value for HTTP request" do |v|
|
|
95
114
|
v = 10 if (v > 10)
|
96
115
|
v
|
97
116
|
end
|
98
|
-
|
99
|
-
usage 'echo "http://github.com/" | ruby bin/example process -c -d " " -t 15 http://solutious.com/'
|
100
|
-
command :processuri do |obj, argv, stdin|
|
117
|
+
command :uri do |obj, argv, stdin|
|
101
118
|
# +cmd+ is the string used to evoke this command. Useful with alias_command (see below).
|
102
119
|
# +stdin+ is either an IO object or a custom object defined with a stdin block (see below)
|
103
120
|
|
@@ -106,8 +123,8 @@ command :processuri do |obj, argv, stdin|
|
|
106
123
|
require 'timeout'
|
107
124
|
|
108
125
|
uris = [stdin, argv].flatten # Combine the argv and stdin arrays
|
109
|
-
delim = obj.delim || ','
|
110
|
-
timeout = obj.timeout || 5
|
126
|
+
delim = obj.option.delim || ','
|
127
|
+
timeout = obj.option.timeout || 5
|
111
128
|
code = :notchecked # The default code when :check is false
|
112
129
|
|
113
130
|
if uris.empty?
|
@@ -117,12 +134,11 @@ command :processuri do |obj, argv, stdin|
|
|
117
134
|
end
|
118
135
|
|
119
136
|
uris.each_with_index do |uri, index|
|
120
|
-
code = response_code(uri, timeout) if (obj.check)
|
137
|
+
code = response_code(uri, timeout) if (obj.option.check)
|
121
138
|
puts [index+1, uri, code].join(delim)
|
122
139
|
end
|
123
140
|
end
|
124
|
-
alias_command :checkuri, :
|
125
|
-
|
141
|
+
alias_command :checkuri, :uri
|
126
142
|
|
127
143
|
|
128
144
|
|
data/drydock.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
@spec = Gem::Specification.new do |s|
|
2
2
|
s.name = %q{drydock}
|
3
|
-
s.version = "0.
|
3
|
+
s.version = "0.4.0"
|
4
4
|
s.specification_version = 1 if s.respond_to? :specification_version=
|
5
5
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
6
6
|
|
@@ -16,30 +16,6 @@
|
|
16
16
|
bin/example
|
17
17
|
drydock.gemspec
|
18
18
|
lib/drydock.rb
|
19
|
-
test/command_test.rb
|
20
|
-
doc
|
21
|
-
doc/classes
|
22
|
-
doc/classes/Drydock
|
23
|
-
doc/classes/Drydock/Command.html
|
24
|
-
doc/classes/Drydock/InvalidArgument.html
|
25
|
-
doc/classes/Drydock/MissingArgument.html
|
26
|
-
doc/classes/Drydock/NoCommandsDefined.html
|
27
|
-
doc/classes/Drydock/UnknownCommand.html
|
28
|
-
doc/classes/Drydock.html
|
29
|
-
doc/created.rid
|
30
|
-
doc/files
|
31
|
-
doc/files/bin
|
32
|
-
doc/files/bin/example.html
|
33
|
-
doc/files/CHANGES_txt.html
|
34
|
-
doc/files/lib
|
35
|
-
doc/files/lib/drydock_rb.html
|
36
|
-
doc/files/LICENSE_txt.html
|
37
|
-
doc/files/README_rdoc.html
|
38
|
-
doc/fr_class_index.html
|
39
|
-
doc/fr_file_index.html
|
40
|
-
doc/fr_method_index.html
|
41
|
-
doc/index.html
|
42
|
-
doc/rdoc-style.css
|
43
19
|
)
|
44
20
|
s.has_rdoc = true
|
45
21
|
s.homepage = %q{http://github.com/delano/drydock}
|
data/lib/drydock.rb
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
require 'optparse'
|
2
2
|
require 'ostruct'
|
3
|
+
require 'stringio'
|
3
4
|
|
4
|
-
#
|
5
|
-
#
|
6
5
|
module Drydock
|
7
6
|
# The base class for all command objects. There is an instance of this class
|
8
7
|
# for every command defined. Global and command-specific options are added
|
@@ -10,11 +9,11 @@ module Drydock
|
|
10
9
|
#
|
11
10
|
# i.e. "example -v date -f yaml"
|
12
11
|
#
|
13
|
-
#
|
12
|
+
# global :v, :verbose, "I want mooooore!"
|
14
13
|
# option :f, :format, String, "Long date format"
|
15
14
|
# command :date do |obj|
|
16
|
-
# puts obj.verbose #=> true
|
17
|
-
# puts obj.format #=> "yaml"
|
15
|
+
# puts obj.global.verbose #=> true
|
16
|
+
# puts obj.option.format #=> "yaml"
|
18
17
|
# end
|
19
18
|
#
|
20
19
|
# You can inherit from this class to create your own: EatFood < Drydock::Command.
|
@@ -23,7 +22,22 @@ module Drydock
|
|
23
22
|
# command :eat => EatFood do |obj|; ...; end
|
24
23
|
#
|
25
24
|
class Command
|
26
|
-
|
25
|
+
VERSION = 0.4
|
26
|
+
# The canonical name of the command (the one used in the command definition). If you
|
27
|
+
# inherit from this class and add a method named +cmd+, you can leave omit the block
|
28
|
+
# in the command definition. That method will be called instead. See bin/examples.
|
29
|
+
attr_reader :cmd
|
30
|
+
# The name used to evoke this command (it's either the canonical name or the alias used).
|
31
|
+
attr_reader :alias
|
32
|
+
# A friendly description of the command.
|
33
|
+
attr_accessor :desc
|
34
|
+
# The block that will be executed when this command is evoked. If the block is nil
|
35
|
+
# it will check if there is a method named +cmd+. If so, that will be executed.
|
36
|
+
attr_reader :b
|
37
|
+
# An OpenStruct object containing the command options specified at run-time.
|
38
|
+
attr_reader :option
|
39
|
+
# An OpenStruct object containing the global options specified at run-time.
|
40
|
+
attr_reader :global
|
27
41
|
|
28
42
|
# The default constructor sets the short name of the command
|
29
43
|
# and stores a reference to the block (if supplied).
|
@@ -35,6 +49,16 @@ module Drydock
|
|
35
49
|
def initialize(cmd, &b)
|
36
50
|
@cmd = (cmd.kind_of?(Symbol)) ? cmd : cmd.to_sym
|
37
51
|
@b = b
|
52
|
+
@option = OpenStruct.new
|
53
|
+
@global = OpenStruct.new
|
54
|
+
|
55
|
+
@global.verbose = 0
|
56
|
+
@global.quiet = false
|
57
|
+
end
|
58
|
+
|
59
|
+
# Returns the command name (not the alias)
|
60
|
+
def name
|
61
|
+
@cmd
|
38
62
|
end
|
39
63
|
|
40
64
|
# Execute the block.
|
@@ -50,14 +74,55 @@ module Drydock
|
|
50
74
|
# +options+ a hash of the command-specific options specific on the command-line.
|
51
75
|
def call(cmd_str=nil, argv=[], stdin=[], global_options={}, options={})
|
52
76
|
@alias = cmd_str.nil? ? @cmd : cmd_str
|
53
|
-
|
54
|
-
|
77
|
+
|
78
|
+
global_options.each_pair do |n,v|
|
79
|
+
self.global.send("#{n}=", v) # Populate the object's globals
|
55
80
|
end
|
56
81
|
|
57
|
-
|
82
|
+
options.each_pair do |n,v|
|
83
|
+
self.option.send("#{n}=", v) # ... and also the command options
|
84
|
+
end
|
85
|
+
|
86
|
+
self.init if self.respond_to? :init # Must be called first!
|
87
|
+
self.print_header if respond_to? :print_header
|
88
|
+
self.valid? if respond_to? :'valid?'
|
58
89
|
|
59
|
-
block_args = [self, argv, stdin]
|
60
|
-
|
90
|
+
block_args = [self, argv, stdin]
|
91
|
+
|
92
|
+
if @b
|
93
|
+
@b.call(*block_args[0..(@b.arity-1)]) # send only as many args as defined
|
94
|
+
elsif self.respond_to? @cmd.to_sym
|
95
|
+
self.send(@cmd)
|
96
|
+
else
|
97
|
+
raise "The command #{@alias} has no block and #{self.class} has no #{@cmd} method!"
|
98
|
+
end
|
99
|
+
|
100
|
+
self.print_footer if respond_to? :print_footer
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
# Print the list of available commands to STDOUT. This is used as the
|
105
|
+
# "default" command unless another default commands is supplied. You
|
106
|
+
# can also write your own Drydock::Command#show_commands to override
|
107
|
+
# this default behaviour.
|
108
|
+
def show_commands
|
109
|
+
project = " for #{Drydock.project}" if Drydock.project?
|
110
|
+
puts "Available commands#{project}:", ""
|
111
|
+
Drydock.commands.keys.sort{ |a,b| a.to_s <=> b.to_s }.each do |cmd|
|
112
|
+
msg = Drydock.commands[cmd].desc
|
113
|
+
|
114
|
+
# Out to sea
|
115
|
+
unless cmd === Drydock.commands[cmd].cmd
|
116
|
+
msg = "See: #{Drydock.decanonize(Drydock.commands[cmd].cmd)} (this is an alias)"
|
117
|
+
end
|
118
|
+
|
119
|
+
puts " %16s: %s" % [Drydock.decanonize(cmd), msg]
|
120
|
+
end
|
121
|
+
|
122
|
+
puts
|
123
|
+
puts "%6s: %s" % ["Try", "#{$0} -h"]
|
124
|
+
puts "%6s %s" % ["", "#{$0} COMMAND -h"]
|
125
|
+
puts
|
61
126
|
end
|
62
127
|
|
63
128
|
# The name of the command
|
@@ -103,34 +168,52 @@ end
|
|
103
168
|
module Drydock
|
104
169
|
extend self
|
105
170
|
|
106
|
-
VERSION = 0.
|
171
|
+
VERSION = 0.4
|
107
172
|
|
108
173
|
private
|
174
|
+
# Disabled. We're going basic, using a module and include/extend.
|
109
175
|
# Stolen from Sinatra!
|
110
|
-
def delegate(*args)
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
end
|
176
|
+
#def delegate(*args)
|
177
|
+
# args.each do |m|
|
178
|
+
# eval(<<-end_eval, binding, "(__Drydock__)", __LINE__)
|
179
|
+
# def #{m}(*args, &b)
|
180
|
+
# Drydock.#{m}(*args, &b)
|
181
|
+
# end
|
182
|
+
# end_eval
|
183
|
+
# end
|
184
|
+
#end
|
185
|
+
#
|
186
|
+
#delegate :before, :after, :alias_command, :desc
|
187
|
+
#delegate :global_option, :global_usage, :usage, :commands, :command
|
188
|
+
#delegate :debug, :option, :stdin, :default, :ignore, :command_alias
|
119
189
|
|
120
|
-
|
121
|
-
delegate :global_option, :global_usage, :usage, :command
|
122
|
-
delegate :debug, :option, :stdin, :default, :ignore, :command_alias
|
190
|
+
@@project = nil
|
123
191
|
|
124
192
|
@@debug = false
|
125
193
|
@@has_run = false
|
126
194
|
@@run = true
|
195
|
+
|
196
|
+
@@global_opts_parser = OptionParser.new
|
197
|
+
@@global_option_names = []
|
198
|
+
|
199
|
+
@@command_opts_parser = []
|
200
|
+
@@command_option_names = []
|
201
|
+
|
127
202
|
@@default_command = nil
|
128
203
|
|
204
|
+
@@commands = {}
|
205
|
+
@@command_descriptions = []
|
206
|
+
@@command_index = 0
|
207
|
+
@@command_index_map = {}
|
208
|
+
|
209
|
+
@@capture = nil # contains one of :stdout, :stderr
|
210
|
+
@@captured = nil
|
211
|
+
|
129
212
|
public
|
130
213
|
# Enable or disable debug output.
|
131
214
|
#
|
132
|
-
#
|
133
|
-
#
|
215
|
+
# debug :on
|
216
|
+
# debug :off
|
134
217
|
#
|
135
218
|
# Calling without :on or :off will toggle the value.
|
136
219
|
#
|
@@ -142,17 +225,44 @@ module Drydock
|
|
142
225
|
@@debug = (!@@debug)
|
143
226
|
end
|
144
227
|
end
|
228
|
+
|
145
229
|
# Returns true if debug output is enabled.
|
146
230
|
def debug?
|
147
231
|
@@debug
|
148
232
|
end
|
149
233
|
|
150
|
-
#
|
234
|
+
# The project of the script. This is currently only used when printing
|
235
|
+
# list of commands (see: Drydock::Command#show_commands). It may be
|
236
|
+
# used elsewhere in the future.
|
237
|
+
def project(txt=nil)
|
238
|
+
return @@project unless txt
|
239
|
+
@@project = txt
|
240
|
+
end
|
241
|
+
|
242
|
+
# Has the project been set?
|
243
|
+
def project?
|
244
|
+
(defined?(@@project) && !@@project.nil?)
|
245
|
+
end
|
246
|
+
|
247
|
+
# Define a default command. You can specify a command name that has
|
248
|
+
# been or will be defined in your script:
|
151
249
|
#
|
152
250
|
# default :task
|
153
251
|
#
|
154
|
-
|
155
|
-
|
252
|
+
# Or you can supply a block which will be used as the default command:
|
253
|
+
#
|
254
|
+
# default do |obj| # This command will be named "default"
|
255
|
+
# # ...
|
256
|
+
# end
|
257
|
+
#
|
258
|
+
# default :hullinspector do # This one will b named "hullinspector"
|
259
|
+
# # ...
|
260
|
+
# end
|
261
|
+
#
|
262
|
+
def default(cmd=nil, &b)
|
263
|
+
raise "Calling default requires a command name or a block" unless cmd || b
|
264
|
+
# Creates the command and returns the name or just stores given name
|
265
|
+
@@default_command = (b) ? command(cmd || :default, &b).cmd : canonize(cmd)
|
156
266
|
end
|
157
267
|
|
158
268
|
# Define a block for processing STDIN before the command is called.
|
@@ -180,30 +290,26 @@ module Drydock
|
|
180
290
|
# Define the default global usage banner. This is displayed
|
181
291
|
# with "script -h".
|
182
292
|
def global_usage(msg)
|
183
|
-
@@
|
184
|
-
global_opts_parser.banner = "USAGE: #{msg}"
|
293
|
+
@@global_opts_parser.banner = "USAGE: #{msg}"
|
185
294
|
end
|
186
295
|
|
187
296
|
# Define a command-specific usage banner. This is displayed
|
188
297
|
# with "script command -h"
|
189
298
|
def usage(msg)
|
190
|
-
|
299
|
+
# The default value given by OptionParser starts with "Usage". That's how
|
300
|
+
# we know we can clear it.
|
301
|
+
get_current_option_parser.banner = "" if get_current_option_parser.banner =~ /^Usage:/
|
302
|
+
get_current_option_parser.banner << "USAGE: #{msg}" << $/
|
191
303
|
end
|
192
304
|
|
193
|
-
# Grab the options parser for the current command or create it if it doesn't exist.
|
194
|
-
def get_current_option_parser
|
195
|
-
@@command_opts_parser ||= []
|
196
|
-
@@command_index ||= 0
|
197
|
-
(@@command_opts_parser[@@command_index] ||= OptionParser.new)
|
198
|
-
end
|
199
305
|
|
200
306
|
# Tell the Drydock parser to ignore something.
|
201
307
|
# Drydock will currently only listen to you if you tell it to "ignore :options",
|
202
308
|
# otherwise it will ignore you!
|
203
309
|
#
|
204
310
|
# +what+ the thing to ignore. When it equals :options Drydock will not parse
|
205
|
-
# the command-specific arguments. It will pass the
|
206
|
-
#
|
311
|
+
# the command-specific arguments. It will pass the arguments directly to the
|
312
|
+
# Command object. This is useful when you want to parse the arguments in some a way
|
207
313
|
# that's too crazy, dangerous for Drydock to handle automatically.
|
208
314
|
def ignore(what=:nothing)
|
209
315
|
@@command_opts_parser[@@command_index] = :ignore if what == :options || what == :all
|
@@ -211,25 +317,38 @@ module Drydock
|
|
211
317
|
|
212
318
|
# Define a global option. See +option+ for more info.
|
213
319
|
def global_option(*args, &b)
|
214
|
-
args.unshift(global_opts_parser)
|
215
|
-
global_option_names << option_parser(args, &b)
|
320
|
+
args.unshift(@@global_opts_parser)
|
321
|
+
@@global_option_names << option_parser(args, &b)
|
216
322
|
end
|
323
|
+
alias :global :global_option
|
217
324
|
|
218
325
|
# Define a command-specific option.
|
219
326
|
#
|
220
327
|
# +args+ is passed directly to OptionParser.on so it can contain anything
|
221
|
-
# that's valid to that method.
|
222
|
-
#
|
223
|
-
#
|
224
|
-
#
|
225
|
-
#
|
226
|
-
#
|
328
|
+
# that's valid to that method. If a class is included, it will tell
|
329
|
+
# OptionParser to expect a value otherwise it assumes a boolean value.
|
330
|
+
# Some examples:
|
331
|
+
#
|
332
|
+
# option :h, :help, "Displays this message"
|
333
|
+
# option '-l x,y,z', '--lang=x,y,z', Array, "Requested languages"
|
334
|
+
#
|
335
|
+
# You can also supply a block to fiddle with the values. The final
|
336
|
+
# value becomes the option's value:
|
337
|
+
#
|
338
|
+
# option :m, :max, Integer, "Maximum threshold" do |v|
|
339
|
+
# v = 100 if v > 100
|
340
|
+
# v
|
341
|
+
# end
|
227
342
|
#
|
228
343
|
# All calls to +option+ must come before the command they're associated
|
229
344
|
# to. Example:
|
230
345
|
#
|
231
|
-
# option :
|
232
|
-
#
|
346
|
+
# option :t, :tasty, "A boolean switch"
|
347
|
+
# option :reason, String, "Requires a parameter"
|
348
|
+
# command :task do |obj|;
|
349
|
+
# obj.options.tasty # => true
|
350
|
+
# obj.options.reason # => I made the sandwich!
|
351
|
+
# end
|
233
352
|
#
|
234
353
|
# When calling your script with a specific command-line option, the value
|
235
354
|
# is available via obj.longname inside the command block.
|
@@ -239,6 +358,9 @@ module Drydock
|
|
239
358
|
current_command_option_names << option_parser(args, &b)
|
240
359
|
end
|
241
360
|
|
361
|
+
|
362
|
+
|
363
|
+
|
242
364
|
# Define a command.
|
243
365
|
#
|
244
366
|
# command :task do
|
@@ -253,20 +375,27 @@ module Drydock
|
|
253
375
|
# end
|
254
376
|
#
|
255
377
|
def command(*cmds, &b)
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
c = cmd.values.first.new(cmd.keys.first, &b)
|
262
|
-
else
|
263
|
-
c = Drydock::Command.new(cmd, &b)
|
264
|
-
end
|
265
|
-
commands[c.cmd] = c
|
266
|
-
command_index_map[c.cmd] = @@command_index
|
267
|
-
@@command_index += 1
|
378
|
+
cmd = cmds.first
|
379
|
+
if cmd.is_a? Hash
|
380
|
+
c = cmd.values.first.new(cmd.keys.first, &b)
|
381
|
+
else
|
382
|
+
c = Drydock::Command.new(cmd, &b)
|
268
383
|
end
|
269
384
|
|
385
|
+
@@command_descriptions[@@command_index] ||= ""
|
386
|
+
|
387
|
+
c.desc = @@command_descriptions[@@command_index]
|
388
|
+
|
389
|
+
# Default Usage Banner.
|
390
|
+
# Without this, there's no help displayed for the command.
|
391
|
+
option_parser = get_option_parser(@@command_index)
|
392
|
+
usage "#{$0} #{c.cmd}" if option_parser.is_a?(OptionParser) && option_parser.banner !~ /^USAGE/
|
393
|
+
|
394
|
+
@@commands[c.cmd] = c
|
395
|
+
@@command_index_map[c.cmd] = @@command_index
|
396
|
+
@@command_index += 1 # This will point to the next command
|
397
|
+
|
398
|
+
c # Return the Command object
|
270
399
|
end
|
271
400
|
|
272
401
|
# Used to create an alias to a defined command.
|
@@ -284,13 +413,32 @@ module Drydock
|
|
284
413
|
# command name that was used via obj.alias.
|
285
414
|
def alias_command(aliaz, cmd)
|
286
415
|
return unless commands.has_key? cmd
|
287
|
-
|
416
|
+
commands[canonize(aliaz)] = commands[cmd]
|
288
417
|
end
|
289
|
-
alias :command_alias :alias_command
|
290
418
|
|
291
|
-
#
|
419
|
+
# Identical to +alias_command+ with reversed arguments.
|
420
|
+
# For whatever reason I forget the order so Drydock supports both.
|
421
|
+
# Tip: the argument order matches the method name.
|
422
|
+
def command_alias(cmd, aliaz)
|
423
|
+
return unless commands.has_key? cmd
|
424
|
+
commands[canonize(aliaz)] = commands[cmd]
|
425
|
+
end
|
426
|
+
|
427
|
+
# A hash of the currently defined Drydock::Command objects
|
292
428
|
def commands
|
293
|
-
@@commands
|
429
|
+
@@commands
|
430
|
+
end
|
431
|
+
|
432
|
+
# An array of the currently defined commands names
|
433
|
+
def command_names
|
434
|
+
@@commands.keys.collect { |cmd| decanonize(cmd); }
|
435
|
+
end
|
436
|
+
|
437
|
+
# Provide a description for a command
|
438
|
+
def desc(txt)
|
439
|
+
@@command_descriptions += [txt]
|
440
|
+
return if get_current_option_parser.is_a?(Symbol)
|
441
|
+
get_current_option_parser.on "ABOUT: #{txt}"
|
294
442
|
end
|
295
443
|
|
296
444
|
# Returns true if automatic execution is enabled.
|
@@ -318,7 +466,7 @@ module Drydock
|
|
318
466
|
return if has_run?
|
319
467
|
@@has_run = true
|
320
468
|
raise NoCommandsDefined.new if commands.empty?
|
321
|
-
|
469
|
+
global_options, cmd_name, command_options, argv = process_arguments(argv)
|
322
470
|
|
323
471
|
cmd_name ||= default_command
|
324
472
|
|
@@ -327,7 +475,9 @@ module Drydock
|
|
327
475
|
stdin = (defined? @@stdin_block) ? @@stdin_block.call(stdin, []) : stdin
|
328
476
|
@@before_block.call if defined? @@before_block
|
329
477
|
|
330
|
-
call_command(cmd_name, argv, stdin)
|
478
|
+
command_portion = lambda { call_command(cmd_name, argv, stdin, global_options, command_options) }
|
479
|
+
|
480
|
+
capture? ? (@@captured = capture_io(@@capture, &command_portion)) : command_portion.call
|
331
481
|
|
332
482
|
@@after_block.call if defined? @@after_block
|
333
483
|
|
@@ -337,33 +487,85 @@ module Drydock
|
|
337
487
|
raise Drydock::MissingArgument.new(ex.args)
|
338
488
|
end
|
339
489
|
|
340
|
-
|
490
|
+
def capture(io)
|
491
|
+
@@capture = io
|
492
|
+
end
|
341
493
|
|
342
|
-
|
343
|
-
|
344
|
-
return unless command?(cmd)
|
345
|
-
get_command(cmd).call(cmd, argv, stdin, @@global_options || {}, @@command_options || {})
|
494
|
+
def captured
|
495
|
+
@@captured
|
346
496
|
end
|
347
497
|
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
498
|
+
def capture?
|
499
|
+
!@@capture.nil?
|
500
|
+
end
|
501
|
+
|
502
|
+
# Grab the options parser for the current command or create it if it doesn't exist.
|
503
|
+
# Returns an instance of OptionParser.
|
504
|
+
def get_current_option_parser
|
505
|
+
(@@command_opts_parser[@@command_index] ||= OptionParser.new)
|
506
|
+
end
|
507
|
+
|
508
|
+
# Grabs the options parser for the given command.
|
509
|
+
# +arg+ can be an index or command name.
|
510
|
+
# Returns an instance of OptionParser.
|
511
|
+
def get_option_parser(arg)
|
512
|
+
index = arg.is_a?(String) ? get_command_index(arg) : arg
|
513
|
+
(@@command_opts_parser[index] ||= OptionParser.new)
|
514
|
+
end
|
353
515
|
|
354
516
|
# Returns true if a command with the name +cmd+ has been defined.
|
355
517
|
def command?(cmd)
|
356
518
|
name = canonize(cmd)
|
357
|
-
|
519
|
+
@@commands.has_key? name
|
358
520
|
end
|
359
521
|
|
360
|
-
# Canonizes a string to the symbol
|
522
|
+
# Canonizes a string (+cmd+) to the symbol for command names
|
523
|
+
# '-' is replaced with '_'
|
361
524
|
def canonize(cmd)
|
362
525
|
return unless cmd
|
363
526
|
return cmd if cmd.kind_of?(Symbol)
|
364
|
-
cmd.tr('-', '_').to_sym
|
527
|
+
cmd.to_s.tr('-', '_').to_sym
|
528
|
+
end
|
529
|
+
|
530
|
+
# Returns a string version of +cmd+, decanonized.
|
531
|
+
# Lowercase, '_' is replaced with '-'
|
532
|
+
def decanonize(cmd)
|
533
|
+
return unless cmd
|
534
|
+
cmd.to_s.tr('_', '-')
|
535
|
+
end
|
536
|
+
|
537
|
+
# Capture STDOUT or STDERR to prevent it from being printed.
|
538
|
+
#
|
539
|
+
# capture(:stdout) do
|
540
|
+
# ...
|
541
|
+
# end
|
542
|
+
#
|
543
|
+
def capture_io(stream)
|
544
|
+
raise "We can only capture STDOUT or STDERR" unless stream == :stdout || stream == :stderr
|
545
|
+
begin
|
546
|
+
eval "$#{stream} = StringIO.new"
|
547
|
+
yield
|
548
|
+
eval("$#{stream}").rewind # Otherwise we'll get nil
|
549
|
+
result = eval("$#{stream}").read
|
550
|
+
ensure
|
551
|
+
eval "$#{stream} = #{stream.to_s.upcase}" # Put it back!
|
552
|
+
end
|
553
|
+
end
|
554
|
+
|
555
|
+
private
|
556
|
+
|
557
|
+
# Executes the block associated to +cmd+
|
558
|
+
def call_command(cmd, argv=[], stdin=nil, global_options={}, command_options={})
|
559
|
+
return unless command?(cmd)
|
560
|
+
get_command(cmd).call(cmd, argv, stdin, global_options || {}, command_options || {})
|
365
561
|
end
|
366
562
|
|
563
|
+
# Returns the Drydock::Command object with the name +cmd+
|
564
|
+
def get_command(cmd)
|
565
|
+
return unless command?(cmd)
|
566
|
+
@@commands[canonize(cmd)]
|
567
|
+
end
|
568
|
+
|
367
569
|
# Processes calls to option and global_option. Symbols are converted into
|
368
570
|
# OptionParser style strings (:h and :help become '-h' and '--help').
|
369
571
|
def option_parser(args=[], &b)
|
@@ -405,14 +607,13 @@ module Drydock
|
|
405
607
|
global_options = command_options = {}
|
406
608
|
cmd = nil
|
407
609
|
|
408
|
-
global_options = global_opts_parser.getopts(argv)
|
409
|
-
|
610
|
+
global_options = @@global_opts_parser.getopts(argv)
|
410
611
|
cmd_name = (argv.empty?) ? @@default_command : argv.shift
|
411
612
|
raise UnknownCommand.new(cmd_name) unless command?(cmd_name)
|
412
613
|
|
413
614
|
cmd = get_command(cmd_name)
|
414
615
|
|
415
|
-
command_parser = @@command_opts_parser[get_command_index(
|
616
|
+
command_parser = @@command_opts_parser[get_command_index(cmd.cmd)]
|
416
617
|
command_options = {}
|
417
618
|
|
418
619
|
# We only need to parse the options out of the arguments when
|
@@ -422,59 +623,47 @@ module Drydock
|
|
422
623
|
command_options = command_parser.getopts(argv)
|
423
624
|
end
|
424
625
|
|
626
|
+
# TODO: Remove this chunk for method creation. We now use OpenStruct.
|
425
627
|
# Add accessors to the Drydock::Command object
|
426
628
|
# for the global and command specific options
|
427
|
-
[global_option_names, (command_option_names[get_command_index(cmd_name)] || [])].flatten.each do |n|
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
end
|
629
|
+
#[global_option_names, (command_option_names[get_command_index(cmd_name)] || [])].flatten.each do |n|
|
630
|
+
# unless cmd.respond_to?(n)
|
631
|
+
# cmd.class.send(:define_method, n) do
|
632
|
+
# instance_variable_get("@#{n}")
|
633
|
+
# end
|
634
|
+
# end
|
635
|
+
# unless cmd.respond_to?("#{n}=")
|
636
|
+
# cmd.class.send(:define_method, "#{n}=") do |val|
|
637
|
+
# instance_variable_set("@#{n}", val)
|
638
|
+
# end
|
639
|
+
# end
|
640
|
+
#end
|
439
641
|
|
440
642
|
[global_options, cmd_name, command_options, argv]
|
441
643
|
end
|
442
644
|
|
443
|
-
|
444
|
-
@@global_option_names ||= []
|
445
|
-
end
|
446
|
-
|
645
|
+
|
447
646
|
# Grab the current list of command-specific option names. This is a list of the
|
448
647
|
# long names.
|
449
648
|
def current_command_option_names
|
450
|
-
@@command_option_names ||= []
|
451
|
-
@@command_index ||= 0
|
452
649
|
(@@command_option_names[@@command_index] ||= [])
|
453
650
|
end
|
454
651
|
|
455
|
-
def command_index_map
|
456
|
-
@@command_index_map ||= {}
|
457
|
-
end
|
458
|
-
|
459
652
|
def get_command_index(cmd)
|
460
|
-
command_index_map[canonize(cmd)] || -1
|
461
|
-
end
|
462
|
-
|
463
|
-
def command_option_names
|
464
|
-
@@command_option_names ||= []
|
465
|
-
end
|
466
|
-
|
467
|
-
def global_opts_parser
|
468
|
-
@@global_opts_parser ||= OptionParser.new
|
653
|
+
@@command_index_map[canonize(cmd)] || -1
|
469
654
|
end
|
470
655
|
|
471
|
-
|
472
|
-
|
473
|
-
|
656
|
+
#
|
657
|
+
# These are the "reel" defaults
|
658
|
+
#
|
659
|
+
@@global_opts_parser.banner = "USAGE: #{$0} [global options] COMMAND [command options]"
|
660
|
+
@@global_opts_parser.on " TRY: #{$0} show-commands #{$/}"
|
661
|
+
@@command_descriptions = ["Display available commands with descriptions"]
|
662
|
+
@@default_command = Drydock.command(:show_commands).cmd
|
474
663
|
|
475
664
|
end
|
476
665
|
|
477
|
-
|
666
|
+
|
478
667
|
|
479
668
|
trap ("SIGINT") do
|
480
669
|
puts "#{$/}Exiting..."
|