drydock 0.3.3 → 0.4.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.
- 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..."
|