drydock 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|