drydock 0.4.0 → 0.5.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 +29 -1
- data/README.rdoc +35 -35
- data/bin/example +33 -18
- data/drydock.gemspec +11 -6
- data/lib/drydock.rb +208 -70
- metadata +4 -4
data/CHANGES.txt
CHANGED
@@ -1,8 +1,36 @@
|
|
1
1
|
DRYDOCK, CHANGES
|
2
2
|
|
3
|
+
#### TODO ###############################
|
4
|
+
|
5
|
+
* Support putting descriptions into resource file (or __END__)
|
6
|
+
* Define aliases with "command [:name, :alias]"
|
7
|
+
* Add Drydock::Console, Drydock::Window, Drydock::Cursor
|
8
|
+
* Generate scripts in the form: script-action
|
9
|
+
* globals can be configured with env vars.
|
10
|
+
* Motivation to stick to a single environment (just stage)
|
11
|
+
* Add convenience methods for system calls: sh, write, read
|
12
|
+
|
13
|
+
|
14
|
+
#### 0.5 (2009-03-11) ###############################
|
15
|
+
|
16
|
+
* NEW: Checks that the command class is a subclass of Drydock::Command
|
17
|
+
* CHANGE: Cleaned up show-commands screen
|
18
|
+
* FIXED: Help didn't work when using command alias
|
19
|
+
* NEW: Named argv values.
|
20
|
+
* CHANGE: argv are now part of the Command class (not passed to command blocks)
|
21
|
+
* CHANGE: "project" now automatically requires the lowercase name of the project
|
22
|
+
and gracefully continues if the require failed.
|
23
|
+
* CHANGE: Drydock will look for different validation method, based on the method
|
24
|
+
being executed. If a validation method is found it's executed and
|
25
|
+
must return a true valid (it can also raise its own exceptions).
|
26
|
+
* NEW: command actions. These are boolean switches with a twist. Drydock looks
|
27
|
+
for command_action or action_command methods. Saves checking the switches
|
28
|
+
and sending to other methods manually.
|
29
|
+
|
30
|
+
|
3
31
|
#### 0.4 (2009-02-28) ###############################
|
4
32
|
|
5
|
-
*
|
33
|
+
* FIXED: "interning empty string" error when bare "option" is used
|
6
34
|
* NEW: Calls valid? method (if present) before calling command block.
|
7
35
|
* NEW: "capture" method. Auto capture STDOUT to obj.stdout etc...
|
8
36
|
* NEW: Automatically calls init and print_header methods before the command
|
data/README.rdoc
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
= Drydock - v0.
|
1
|
+
= Drydock - v0.5
|
2
2
|
|
3
3
|
Inspired by github-gem and bmizerany-frylock.
|
4
4
|
|
5
5
|
== Overview
|
6
6
|
|
7
|
-
Drydock is a seaworthy DSL for command line apps. It
|
7
|
+
Drydock is a seaworthy DSL for command line apps. It's contained in a single .rb file so it's easy to copy directly into your project.
|
8
8
|
|
9
9
|
== Install
|
10
10
|
|
@@ -32,41 +32,41 @@ See bin/example for more.
|
|
32
32
|
end
|
33
33
|
|
34
34
|
desc "A friendly welcome to the Drydock"
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
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
|
41
54
|
|
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
|
-
|
55
55
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
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
|
61
|
+
|
62
|
+
desc "Do something with John West's Smoked Oysters"
|
63
|
+
command :oysters => JohnWestSmokedOysters do |obj|
|
64
|
+
p obj # => #<JohnWestSmokedOysters:0x42179c ... >
|
65
|
+
end
|
66
|
+
|
67
|
+
desc "My way of saying hello!"
|
68
|
+
command :ahoy! => JohnWestSmokedOysters
|
69
|
+
# If you don't provide a block, Drydock will call JohnWestSmokedOysters#ahoy!
|
70
70
|
|
71
71
|
|
72
72
|
== More Information
|
data/bin/example
CHANGED
@@ -4,11 +4,17 @@
|
|
4
4
|
#
|
5
5
|
# This is a functioning script so you can copy it, run it,
|
6
6
|
# and just generally be a longshoreman about things. This is
|
7
|
-
# a
|
7
|
+
# a drydock after all.
|
8
8
|
#
|
9
|
-
# If you're reading this via the Rdocs you won't see the code.
|
9
|
+
# If you're reading this via the Rdocs you won't see the code. See:
|
10
|
+
#
|
11
|
+
# http://github.com/delano/drydock/blob/drydock-0.5.0/bin/example
|
12
|
+
#
|
13
|
+
# For an example of a complex command-line application using
|
14
|
+
# Drydock, see:
|
15
|
+
#
|
16
|
+
# http://github.com/solutious/rudy
|
10
17
|
#
|
11
|
-
# http://github.com/delano/drydock/blob/master/bin/example
|
12
18
|
|
13
19
|
$:.unshift File.expand_path(File.join(File.dirname(__FILE__), '..')), 'lib'
|
14
20
|
|
@@ -33,9 +39,8 @@ end
|
|
33
39
|
|
34
40
|
desc "A friendly welcome to the Drydock"
|
35
41
|
command :welcome do
|
36
|
-
puts "Welcome to Drydock."
|
37
|
-
puts "For available commands:"
|
38
|
-
puts "#{$0} show-commands"
|
42
|
+
puts "Welcome to Drydock.", $/
|
43
|
+
puts "For available commands: #{$0} show-commands"
|
39
44
|
end
|
40
45
|
|
41
46
|
usage "USAGE: #{$0} laugh [-f]"
|
@@ -63,8 +68,7 @@ end
|
|
63
68
|
|
64
69
|
usage "#{$0} [-s] [-vv] date"
|
65
70
|
desc "Display the current date"
|
66
|
-
command :date do |obj
|
67
|
-
# +argv+ is an array containing the unnamed arguments
|
71
|
+
command :date do |obj|
|
68
72
|
require 'time'
|
69
73
|
now = Time.now
|
70
74
|
puts "(Not verbose enough. Try adding a -v.)" if (obj.global.verbose || 0) == 1
|
@@ -75,14 +79,15 @@ end
|
|
75
79
|
|
76
80
|
ignore :options
|
77
81
|
desc "This command ignores options"
|
78
|
-
command :rogue do |obj
|
82
|
+
command :rogue do |obj|
|
79
83
|
# You can use ignore :options to tell Drydock to not process the
|
80
84
|
# command-specific options.
|
81
|
-
|
85
|
+
# Unnamed arguments are available from obj.argv
|
86
|
+
if obj.argv.empty?
|
82
87
|
puts "Had you supplied some arguments, I would have ignored them."
|
83
88
|
else
|
84
89
|
puts "Hi! You supplied some arguments but I ignored them."
|
85
|
-
puts "They're all still here in this array: %s" % argv.join(', ')
|
90
|
+
puts "They're all still here in this array: %s" % obj.argv.join(', ')
|
86
91
|
end
|
87
92
|
end
|
88
93
|
|
@@ -102,9 +107,10 @@ command :ahoy! => JohnWestSmokedOysters
|
|
102
107
|
# If you don't provide a block, Drydock will call JohnWestSmokedOysters#ahoy!
|
103
108
|
|
104
109
|
|
110
|
+
require 'yaml'
|
105
111
|
|
106
|
-
usage 'ruby bin/example
|
107
|
-
usage 'echo "http://solutious.com/" | ruby bin/example
|
112
|
+
usage 'ruby bin/example uri -c -d " " -t 15 http://solutious.com/'
|
113
|
+
usage 'echo "http://solutious.com/" | ruby bin/example uri -c -d " " -t 15'
|
108
114
|
desc "Check for broken URIs"
|
109
115
|
option :c, :check, "Check response codes for each URI"
|
110
116
|
option :d, :delim, String, "Output delimiter"
|
@@ -114,18 +120,23 @@ option :t, :timeout, Float, "Timeout value for HTTP request" do |v|
|
|
114
120
|
v = 10 if (v > 10)
|
115
121
|
v
|
116
122
|
end
|
117
|
-
|
118
|
-
|
119
|
-
#
|
123
|
+
argv :uris
|
124
|
+
command :uri do |obj|
|
125
|
+
# This command processes the output of the stdin block (below this definition).
|
126
|
+
# The output of that block is available as obj.stdin. If there is no stdin block
|
127
|
+
# obj.stdin will be STDIN's IO object.
|
120
128
|
|
121
129
|
require 'net/http'
|
122
130
|
require 'uri'
|
123
131
|
require 'timeout'
|
124
132
|
|
125
|
-
uris = [
|
133
|
+
uris = []
|
134
|
+
uris += obj.stdin if obj.stdin
|
135
|
+
uris += obj.argv.uris if obj.argv.uris
|
136
|
+
|
126
137
|
delim = obj.option.delim || ','
|
127
138
|
timeout = obj.option.timeout || 5
|
128
|
-
code = :notchecked
|
139
|
+
code = :notchecked # The default code when :check is false
|
129
140
|
|
130
141
|
if uris.empty?
|
131
142
|
puts "Frylock: You didn't provide any URIs. "
|
@@ -137,6 +148,10 @@ command :uri do |obj, argv, stdin|
|
|
137
148
|
code = response_code(uri, timeout) if (obj.option.check)
|
138
149
|
puts [index+1, uri, code].join(delim)
|
139
150
|
end
|
151
|
+
|
152
|
+
# NOTE: The alias used to evoke this command is available
|
153
|
+
# via obj.alias
|
154
|
+
|
140
155
|
end
|
141
156
|
alias_command :checkuri, :uri
|
142
157
|
|
data/drydock.gemspec
CHANGED
@@ -1,13 +1,17 @@
|
|
1
1
|
@spec = Gem::Specification.new do |s|
|
2
2
|
s.name = %q{drydock}
|
3
|
-
s.version = "0.
|
3
|
+
s.version = "0.5.0"
|
4
|
+
s.date = %q{2009-03-11}
|
4
5
|
s.specification_version = 1 if s.respond_to? :specification_version=
|
5
6
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
6
7
|
|
7
8
|
s.authors = ["Delano Mandelbaum"]
|
8
|
-
s.
|
9
|
-
s.
|
9
|
+
s.description = %q{A seaworthy DSL for writing command line apps}
|
10
|
+
s.summary = s.description
|
10
11
|
s.email = %q{delano@solutious.com}
|
12
|
+
|
13
|
+
# = MANIFEST =
|
14
|
+
# git ls-files
|
11
15
|
s.files = %w(
|
12
16
|
CHANGES.txt
|
13
17
|
LICENSE.txt
|
@@ -17,13 +21,14 @@
|
|
17
21
|
drydock.gemspec
|
18
22
|
lib/drydock.rb
|
19
23
|
)
|
24
|
+
|
25
|
+
# s.add_dependency ''
|
26
|
+
|
20
27
|
s.has_rdoc = true
|
21
28
|
s.homepage = %q{http://github.com/delano/drydock}
|
22
29
|
s.extra_rdoc_files = %w[README.rdoc LICENSE.txt CHANGES.txt]
|
23
|
-
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Drydock:
|
30
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Drydock: #{s.description}", "--main", "README.rdoc"]
|
24
31
|
s.require_paths = ["lib"]
|
25
32
|
s.rubygems_version = %q{1.1.1}
|
26
|
-
s.summary = %q{A seaworthy DSL for writing command line apps}
|
27
|
-
|
28
33
|
s.rubyforge_project = "drydock"
|
29
34
|
end
|
data/lib/drydock.rb
CHANGED
@@ -3,34 +3,64 @@ require 'ostruct'
|
|
3
3
|
require 'stringio'
|
4
4
|
|
5
5
|
module Drydock
|
6
|
+
class FancyArray < Array #:nodoc:
|
7
|
+
attr_reader :fields
|
8
|
+
def add_field(n)
|
9
|
+
@fields ||= []
|
10
|
+
field_name = n
|
11
|
+
eval <<-RUBY, binding, '(Drydock::FancyArray)', 1
|
12
|
+
def #{n}
|
13
|
+
if self.size > @fields.size && '#{n}'.to_sym == @fields.last
|
14
|
+
self[#{@fields.size}..-1]
|
15
|
+
else
|
16
|
+
self[#{@fields.size}]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
RUBY
|
20
|
+
@fields << n
|
21
|
+
n
|
22
|
+
end
|
23
|
+
def fields=(*args)
|
24
|
+
args.flatten.each do |field|
|
25
|
+
add_field(field)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
|
6
31
|
# The base class for all command objects. There is an instance of this class
|
7
32
|
# for every command defined. Global and command-specific options are added
|
8
33
|
# as attributes to this class dynamically.
|
9
34
|
#
|
10
|
-
#
|
35
|
+
# i.e. "example -v select --location kumamoto"
|
11
36
|
#
|
12
37
|
# global :v, :verbose, "I want mooooore!"
|
13
|
-
# option :
|
14
|
-
# command :
|
15
|
-
#
|
16
|
-
#
|
38
|
+
# option :l, :location, String, "Source location"
|
39
|
+
# command :select do |obj|
|
40
|
+
# puts obj.global.verbose #=> true
|
41
|
+
# puts obj.option.location #=> "kumamoto"
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# You can sub-class it to create your own:
|
45
|
+
#
|
46
|
+
# class Malpeque < Drydock::Command
|
47
|
+
# # ... sea to it
|
17
48
|
# end
|
18
49
|
#
|
19
|
-
#
|
20
|
-
# And then specific your class in the command definition:
|
50
|
+
# And then specify your class in the command definition:
|
21
51
|
#
|
22
|
-
# command :eat =>
|
52
|
+
# command :eat => Malpeque do |obj|
|
53
|
+
# # ... do stuff with your obj
|
54
|
+
# end
|
23
55
|
#
|
24
56
|
class Command
|
25
|
-
VERSION = 0.
|
57
|
+
VERSION = 0.5
|
26
58
|
# The canonical name of the command (the one used in the command definition). If you
|
27
59
|
# inherit from this class and add a method named +cmd+, you can leave omit the block
|
28
60
|
# in the command definition. That method will be called instead. See bin/examples.
|
29
61
|
attr_reader :cmd
|
30
62
|
# The name used to evoke this command (it's either the canonical name or the alias used).
|
31
63
|
attr_reader :alias
|
32
|
-
# A friendly description of the command.
|
33
|
-
attr_accessor :desc
|
34
64
|
# The block that will be executed when this command is evoked. If the block is nil
|
35
65
|
# it will check if there is a method named +cmd+. If so, that will be executed.
|
36
66
|
attr_reader :b
|
@@ -38,6 +68,15 @@ module Drydock
|
|
38
68
|
attr_reader :option
|
39
69
|
# An OpenStruct object containing the global options specified at run-time.
|
40
70
|
attr_reader :global
|
71
|
+
# A friendly description of the command.
|
72
|
+
attr_accessor :desc
|
73
|
+
# An array of action names specified in the command definition
|
74
|
+
attr_accessor :actions
|
75
|
+
# An instance of Drydock::FancyArray. Acts like an array of unnamed arguments
|
76
|
+
# but also allows field names if supplied.
|
77
|
+
attr_accessor :argv
|
78
|
+
# Either an IO handle to STDIN or the output of the Drydock#stdin handler.
|
79
|
+
attr_reader :stdin
|
41
80
|
|
42
81
|
# The default constructor sets the short name of the command
|
43
82
|
# and stores a reference to the block (if supplied).
|
@@ -49,6 +88,9 @@ module Drydock
|
|
49
88
|
def initialize(cmd, &b)
|
50
89
|
@cmd = (cmd.kind_of?(Symbol)) ? cmd : cmd.to_sym
|
51
90
|
@b = b
|
91
|
+
@actions = []
|
92
|
+
@argv = Drydock::FancyArray.new # an array with field names
|
93
|
+
@stdin = STDIN
|
52
94
|
@option = OpenStruct.new
|
53
95
|
@global = OpenStruct.new
|
54
96
|
|
@@ -65,13 +107,13 @@ module Drydock
|
|
65
107
|
#
|
66
108
|
# Calls self.init before calling the block. Implement this method when
|
67
109
|
#
|
68
|
-
#
|
69
|
-
# unless an alias was used used to evoke this command
|
70
|
-
#
|
71
|
-
# will contain the arguments exactly as they were defined on the command-line
|
72
|
-
#
|
73
|
-
#
|
74
|
-
#
|
110
|
+
# <li>+cmd_str+ is the short name used to evoke this command. It will equal @cmd
|
111
|
+
# unless an alias was used used to evoke this command.</li>
|
112
|
+
# <li>+argv+ an array of unnamed arguments. If ignore :options was declared this</li>
|
113
|
+
# will contain the arguments exactly as they were defined on the command-line.</li>
|
114
|
+
# <li>+stdin+ contains the output of stdin do; ...; end otherwise it's a STDIN IO handle.</li>
|
115
|
+
# <li>+global_options+ a hash of the global options specified on the command-line</li>
|
116
|
+
# <li>+options+ a hash of the command-specific options specific on the command-line.</li>
|
75
117
|
def call(cmd_str=nil, argv=[], stdin=[], global_options={}, options={})
|
76
118
|
@alias = cmd_str.nil? ? @cmd : cmd_str
|
77
119
|
|
@@ -83,15 +125,35 @@ module Drydock
|
|
83
125
|
self.option.send("#{n}=", v) # ... and also the command options
|
84
126
|
end
|
85
127
|
|
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?'
|
89
128
|
|
90
|
-
|
129
|
+
@argv << argv # TODO: Using += returns an Array instead of FancyArray
|
130
|
+
@argv.flatten! # NOTE: << creates @argv[[]]
|
131
|
+
@stdin = stdin
|
132
|
+
|
133
|
+
self.init if self.respond_to? :init # Must be called first!
|
134
|
+
self.print_header if self.respond_to? :print_header
|
91
135
|
|
92
136
|
if @b
|
93
|
-
|
137
|
+
run_validation
|
138
|
+
@b.call(self)
|
139
|
+
|
140
|
+
elsif !(chosen = find_action(options)).empty?
|
141
|
+
raise "Only one action at a time please! I can't #{chosen.join(' AND ')}." if chosen.size > 1
|
142
|
+
criteria = [[@cmd, chosen.first], [chosen.first, @cmd]]
|
143
|
+
meth = name = nil
|
144
|
+
# Try command_action, then action_command
|
145
|
+
criteria.each do |tuple|
|
146
|
+
name = tuple.join('_')
|
147
|
+
meth = name if self.respond_to?(name)
|
148
|
+
end
|
149
|
+
|
150
|
+
raise "#{self.class} needs a #{name} method!" unless meth
|
151
|
+
|
152
|
+
run_validation(meth)
|
153
|
+
self.send(meth)
|
154
|
+
|
94
155
|
elsif self.respond_to? @cmd.to_sym
|
156
|
+
run_validation(@cmd)
|
95
157
|
self.send(@cmd)
|
96
158
|
else
|
97
159
|
raise "The command #{@alias} has no block and #{self.class} has no #{@cmd} method!"
|
@@ -101,6 +163,40 @@ module Drydock
|
|
101
163
|
|
102
164
|
end
|
103
165
|
|
166
|
+
# <li>+meth+ The method name used to determine the name of the validation method.
|
167
|
+
# If not supplied, the validation method is "valid?" otherwise it's "meth_valid?"</li>
|
168
|
+
# If the command class doesn't have the given validation method, we'll just continue
|
169
|
+
# on our way.
|
170
|
+
#
|
171
|
+
# Recognized validation methods are:
|
172
|
+
#
|
173
|
+
# def valid? # if we're executing a command block
|
174
|
+
# def command_valid? # if we're executing an object method
|
175
|
+
# def command_action_valid? # if the main meth is command_action
|
176
|
+
# def action_command_valid? # if the main meth is action_command
|
177
|
+
#
|
178
|
+
# This method raises a generic exception when the validation method returns false.
|
179
|
+
# However, <strong>it's more appropriate for the validation methods to raise
|
180
|
+
# detailed exceptions</strong>.
|
181
|
+
#
|
182
|
+
def run_validation(meth=nil)
|
183
|
+
vmeth = meth ? [meth, 'valid?'].join('_') : 'valid?'
|
184
|
+
is_valid = self.respond_to?(vmeth) ? self.send(vmeth) : true
|
185
|
+
raise "Your request is not valid. See #{$0} #{@cmd} -h" unless is_valid
|
186
|
+
end
|
187
|
+
private :run_validation
|
188
|
+
|
189
|
+
# Compares the list of known actions to the list of boolean switches supplied
|
190
|
+
# on the command line (if any).
|
191
|
+
# <li>+options+ is a hash of the named command line arguments (created by
|
192
|
+
# OptionParser#getopts)</li>
|
193
|
+
# Returns an array of action names (empty if no action was supplied)
|
194
|
+
def find_action(options)
|
195
|
+
boolkeys = options.keys.select { |n| options[n] == true } || []
|
196
|
+
(@actions || []) & boolkeys # an array of requested actions (or empty)
|
197
|
+
end
|
198
|
+
private :find_action
|
199
|
+
|
104
200
|
# Print the list of available commands to STDOUT. This is used as the
|
105
201
|
# "default" command unless another default commands is supplied. You
|
106
202
|
# can also write your own Drydock::Command#show_commands to override
|
@@ -108,17 +204,25 @@ module Drydock
|
|
108
204
|
def show_commands
|
109
205
|
project = " for #{Drydock.project}" if Drydock.project?
|
110
206
|
puts "Available commands#{project}:", ""
|
111
|
-
|
112
|
-
|
113
|
-
|
207
|
+
cmds = {}
|
208
|
+
Drydock.commands.keys.each do |cmd|
|
209
|
+
pretty = Drydock.decanonize(cmd)
|
114
210
|
# Out to sea
|
211
|
+
cmds[Drydock.commands[cmd].cmd] ||= {}
|
115
212
|
unless cmd === Drydock.commands[cmd].cmd
|
116
|
-
|
213
|
+
(cmds[Drydock.commands[cmd].cmd][:aliases] ||= []) << pretty
|
214
|
+
next
|
117
215
|
end
|
118
|
-
|
119
|
-
|
216
|
+
cmds[cmd][:desc] = Drydock.commands[cmd].desc
|
217
|
+
cmds[cmd][:pretty] = cmd
|
120
218
|
end
|
121
|
-
|
219
|
+
|
220
|
+
cmds.keys.sort{ |a,b| a.to_s <=> b.to_s }.each do |cmd|
|
221
|
+
p = cmds[cmd]
|
222
|
+
puts " %16s: %s" % [p[:pretty], p[:desc]]
|
223
|
+
puts " %17s (%s: %s)" % ['', "aliases", cmds[cmd][:aliases].join(', ')] if cmds[cmd][:aliases]
|
224
|
+
end
|
225
|
+
|
122
226
|
puts
|
123
227
|
puts "%6s: %s" % ["Try", "#{$0} -h"]
|
124
228
|
puts "%6s %s" % ["", "#{$0} COMMAND -h"]
|
@@ -170,23 +274,6 @@ module Drydock
|
|
170
274
|
|
171
275
|
VERSION = 0.4
|
172
276
|
|
173
|
-
private
|
174
|
-
# Disabled. We're going basic, using a module and include/extend.
|
175
|
-
# Stolen from Sinatra!
|
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
|
189
|
-
|
190
277
|
@@project = nil
|
191
278
|
|
192
279
|
@@debug = false
|
@@ -198,6 +285,7 @@ module Drydock
|
|
198
285
|
|
199
286
|
@@command_opts_parser = []
|
200
287
|
@@command_option_names = []
|
288
|
+
@@command_actions = []
|
201
289
|
|
202
290
|
@@default_command = nil
|
203
291
|
|
@@ -205,6 +293,7 @@ module Drydock
|
|
205
293
|
@@command_descriptions = []
|
206
294
|
@@command_index = 0
|
207
295
|
@@command_index_map = {}
|
296
|
+
@@command_argv_names = [] # an array of names for values of argv
|
208
297
|
|
209
298
|
@@capture = nil # contains one of :stdout, :stderr
|
210
299
|
@@captured = nil
|
@@ -231,11 +320,31 @@ module Drydock
|
|
231
320
|
@@debug
|
232
321
|
end
|
233
322
|
|
323
|
+
# Provide names for CLI arguments, in the order they appear.
|
324
|
+
#
|
325
|
+
# $ yourscript sample malpeque zinqy
|
326
|
+
# argv :name, :flavour
|
327
|
+
# command :sample do |obj|
|
328
|
+
# obj.argv.name # => malpeque
|
329
|
+
# obj.argv.flavour # => zinqy
|
330
|
+
# end
|
331
|
+
#
|
332
|
+
def argv(*args)
|
333
|
+
@@command_argv_names[@@command_index] ||= []
|
334
|
+
@@command_argv_names[@@command_index] += args.flatten
|
335
|
+
end
|
336
|
+
|
234
337
|
# The project of the script. This is currently only used when printing
|
235
338
|
# list of commands (see: Drydock::Command#show_commands). It may be
|
236
339
|
# used elsewhere in the future.
|
237
340
|
def project(txt=nil)
|
341
|
+
|
238
342
|
return @@project unless txt
|
343
|
+
|
344
|
+
begin
|
345
|
+
require txt.downcase
|
346
|
+
rescue LoadError
|
347
|
+
end
|
239
348
|
@@project = txt
|
240
349
|
end
|
241
350
|
|
@@ -255,7 +364,7 @@ module Drydock
|
|
255
364
|
# # ...
|
256
365
|
# end
|
257
366
|
#
|
258
|
-
# default :hullinspector do # This one will
|
367
|
+
# default :hullinspector do # This one will be named "hullinspector"
|
259
368
|
# # ...
|
260
369
|
# end
|
261
370
|
#
|
@@ -266,9 +375,11 @@ module Drydock
|
|
266
375
|
end
|
267
376
|
|
268
377
|
# Define a block for processing STDIN before the command is called.
|
269
|
-
# The command block receives the return value of this block
|
378
|
+
# The command block receives the return value of this block as obj.stdin:
|
270
379
|
#
|
271
|
-
# command :task do |obj
|
380
|
+
# command :task do |obj|;
|
381
|
+
# obj.stdin # => ...
|
382
|
+
# end
|
272
383
|
#
|
273
384
|
# If a stdin block isn't defined, +stdin+ above will be the STDIN IO handle.
|
274
385
|
def stdin(&b)
|
@@ -358,9 +469,23 @@ module Drydock
|
|
358
469
|
current_command_option_names << option_parser(args, &b)
|
359
470
|
end
|
360
471
|
|
361
|
-
|
362
|
-
|
363
|
-
|
472
|
+
# Define an command-specific action.
|
473
|
+
#
|
474
|
+
# This is functionality very similar to option, but with an exciting and buoyant twist:
|
475
|
+
# Drydock keeps track of actions for each command (in addition to treating it like an option).
|
476
|
+
# When an action is specifiec on the command line Drydock looks for command_action or
|
477
|
+
# action_command methods in the command class.
|
478
|
+
#
|
479
|
+
# action :E, :eat, "Eat something"
|
480
|
+
# command :oysters => Fresh::Oysters
|
481
|
+
#
|
482
|
+
# # Drydock will look for Fresh::Oysters#eat_oysters and Fresh::Oysters#oysters_eat.
|
483
|
+
#
|
484
|
+
def action(*args, &b)
|
485
|
+
ret = option(*args, &b) # returns an array of all the current option names
|
486
|
+
current_command_action << ret.last # the most recent is last
|
487
|
+
end
|
488
|
+
|
364
489
|
# Define a command.
|
365
490
|
#
|
366
491
|
# command :task do
|
@@ -376,15 +501,24 @@ module Drydock
|
|
376
501
|
#
|
377
502
|
def command(*cmds, &b)
|
378
503
|
cmd = cmds.first
|
504
|
+
|
379
505
|
if cmd.is_a? Hash
|
380
|
-
|
506
|
+
raise "#{cmd.values.first} is not a subclass of Drydock::Command" unless cmd.values.first.ancestors.member?(Drydock::Command)
|
507
|
+
c = cmd.values.first.new(cmd.keys.first, &b) # A custom class was specified
|
508
|
+
# TODO: handle command [:task, :alias] => Class
|
509
|
+
#elsif cmd.is_a? Array
|
510
|
+
# p cmd
|
381
511
|
else
|
382
512
|
c = Drydock::Command.new(cmd, &b)
|
383
513
|
end
|
384
514
|
|
385
515
|
@@command_descriptions[@@command_index] ||= ""
|
516
|
+
@@command_actions[@@command_index] ||= []
|
517
|
+
@@command_argv_names[@@command_index] ||= []
|
386
518
|
|
387
519
|
c.desc = @@command_descriptions[@@command_index]
|
520
|
+
c.actions = @@command_actions[@@command_index]
|
521
|
+
c.argv.fields = @@command_argv_names[@@command_index]
|
388
522
|
|
389
523
|
# Default Usage Banner.
|
390
524
|
# Without this, there's no help displayed for the command.
|
@@ -406,8 +540,8 @@ module Drydock
|
|
406
540
|
#
|
407
541
|
# Either name can be used on the command-line:
|
408
542
|
#
|
409
|
-
# $
|
410
|
-
# $
|
543
|
+
# $ yourscript task [options]
|
544
|
+
# $ yourscript pointer [options]
|
411
545
|
#
|
412
546
|
# Inside of the command definition, you have access to the
|
413
547
|
# command name that was used via obj.alias.
|
@@ -499,20 +633,6 @@ module Drydock
|
|
499
633
|
!@@capture.nil?
|
500
634
|
end
|
501
635
|
|
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
|
515
|
-
|
516
636
|
# Returns true if a command with the name +cmd+ has been defined.
|
517
637
|
def command?(cmd)
|
518
638
|
name = canonize(cmd)
|
@@ -649,10 +769,28 @@ module Drydock
|
|
649
769
|
(@@command_option_names[@@command_index] ||= [])
|
650
770
|
end
|
651
771
|
|
772
|
+
def current_command_action
|
773
|
+
(@@command_actions[@@command_index] ||= [])
|
774
|
+
end
|
775
|
+
|
652
776
|
def get_command_index(cmd)
|
653
777
|
@@command_index_map[canonize(cmd)] || -1
|
654
778
|
end
|
655
779
|
|
780
|
+
# Grab the options parser for the current command or create it if it doesn't exist.
|
781
|
+
# Returns an instance of OptionParser.
|
782
|
+
def get_current_option_parser
|
783
|
+
(@@command_opts_parser[@@command_index] ||= OptionParser.new)
|
784
|
+
end
|
785
|
+
|
786
|
+
# Grabs the options parser for the given command.
|
787
|
+
# +arg+ can be an index or command name.
|
788
|
+
# Returns an instance of OptionParser.
|
789
|
+
def get_option_parser(arg)
|
790
|
+
index = arg.is_a?(String) ? get_command_index(arg) : arg
|
791
|
+
(@@command_opts_parser[index] ||= OptionParser.new)
|
792
|
+
end
|
793
|
+
|
656
794
|
#
|
657
795
|
# These are the "reel" defaults
|
658
796
|
#
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: drydock
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Delano Mandelbaum
|
@@ -9,11 +9,11 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date:
|
12
|
+
date: 2009-03-11 00:00:00 -04:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
16
|
-
description: A seaworthy DSL for writing command line apps
|
16
|
+
description: A seaworthy DSL for writing command line apps
|
17
17
|
email: delano@solutious.com
|
18
18
|
executables: []
|
19
19
|
|
@@ -38,7 +38,7 @@ rdoc_options:
|
|
38
38
|
- --line-numbers
|
39
39
|
- --inline-source
|
40
40
|
- --title
|
41
|
-
- "Drydock:
|
41
|
+
- "Drydock: A seaworthy DSL for writing command line apps"
|
42
42
|
- --main
|
43
43
|
- README.rdoc
|
44
44
|
require_paths:
|