delano-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 +24 -1
- data/README.rdoc +34 -34
- data/bin/example +33 -18
- data/drydock.gemspec +4 -5
- data/lib/drydock.rb +205 -70
- metadata +4 -4
data/CHANGES.txt
CHANGED
|
@@ -1,8 +1,31 @@
|
|
|
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
|
+
|
|
9
|
+
|
|
10
|
+
#### 0.5 (2009-03-01) ###############################
|
|
11
|
+
|
|
12
|
+
* CHANGE: Cleaned up show-commands screen
|
|
13
|
+
* FIXED: Help didn't work when using command alias
|
|
14
|
+
* NEW: Named argv values.
|
|
15
|
+
* CHANGE: argv are now part of the Command class (not passed to command blocks)
|
|
16
|
+
* CHANGE: "project" now automatically requires the lowercase name of the project
|
|
17
|
+
and gracefully continues if the require failed.
|
|
18
|
+
* CHANGE: Drydock will look for different validation method, based on the method
|
|
19
|
+
being executed. If a validation method is found it's executed and
|
|
20
|
+
must return a true valid (it can also raise its own exceptions).
|
|
21
|
+
* NEW: command actions. These are boolean switches with a twist. Drydock looks
|
|
22
|
+
for command_action or action_command methods. Saves checking the switches
|
|
23
|
+
and sending to other methods manually.
|
|
24
|
+
|
|
25
|
+
|
|
3
26
|
#### 0.4 (2009-02-28) ###############################
|
|
4
27
|
|
|
5
|
-
*
|
|
28
|
+
* FIXED: "interning empty string" error when bare "option" is used
|
|
6
29
|
* NEW: Calls valid? method (if present) before calling command block.
|
|
7
30
|
* NEW: "capture" method. Auto capture STDOUT to obj.stdout etc...
|
|
8
31
|
* NEW: Automatically calls init and print_header methods before the command
|
data/README.rdoc
CHANGED
|
@@ -4,7 +4,7 @@ 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,12 +1,12 @@
|
|
|
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-01}
|
|
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.description = %q{A seaworthy DSL for writing command line apps inspired by Blake Mizerany's Frylock}
|
|
9
|
+
s.description = %q{A seaworthy DSL for writing command line apps}
|
|
10
10
|
s.email = %q{delano@solutious.com}
|
|
11
11
|
s.files = %w(
|
|
12
12
|
CHANGES.txt
|
|
@@ -23,7 +23,6 @@
|
|
|
23
23
|
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Drydock: a seaworthy DSL for command-line apps", "--main", "README.rdoc"]
|
|
24
24
|
s.require_paths = ["lib"]
|
|
25
25
|
s.rubygems_version = %q{1.1.1}
|
|
26
|
-
s.summary = %q{A seaworthy DSL for writing command line apps}
|
|
27
|
-
|
|
26
|
+
s.summary = %q{Drydock: A seaworthy DSL for writing command line apps}
|
|
28
27
|
s.rubyforge_project = "drydock"
|
|
29
28
|
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,29 @@ 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)
|
|
238
341
|
return @@project unless txt
|
|
342
|
+
begin
|
|
343
|
+
require txt.downcase
|
|
344
|
+
rescue LoadError
|
|
345
|
+
end
|
|
239
346
|
@@project = txt
|
|
240
347
|
end
|
|
241
348
|
|
|
@@ -255,7 +362,7 @@ module Drydock
|
|
|
255
362
|
# # ...
|
|
256
363
|
# end
|
|
257
364
|
#
|
|
258
|
-
# default :hullinspector do # This one will
|
|
365
|
+
# default :hullinspector do # This one will be named "hullinspector"
|
|
259
366
|
# # ...
|
|
260
367
|
# end
|
|
261
368
|
#
|
|
@@ -266,9 +373,11 @@ module Drydock
|
|
|
266
373
|
end
|
|
267
374
|
|
|
268
375
|
# Define a block for processing STDIN before the command is called.
|
|
269
|
-
# The command block receives the return value of this block
|
|
376
|
+
# The command block receives the return value of this block as obj.stdin:
|
|
270
377
|
#
|
|
271
|
-
# command :task do |obj
|
|
378
|
+
# command :task do |obj|;
|
|
379
|
+
# obj.stdin # => ...
|
|
380
|
+
# end
|
|
272
381
|
#
|
|
273
382
|
# If a stdin block isn't defined, +stdin+ above will be the STDIN IO handle.
|
|
274
383
|
def stdin(&b)
|
|
@@ -358,9 +467,23 @@ module Drydock
|
|
|
358
467
|
current_command_option_names << option_parser(args, &b)
|
|
359
468
|
end
|
|
360
469
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
470
|
+
# Define an command-specific action.
|
|
471
|
+
#
|
|
472
|
+
# This is functionality very similar to option, but with an exciting and buoyant twist:
|
|
473
|
+
# Drydock keeps track of actions for each command (in addition to treating it like an option).
|
|
474
|
+
# When an action is specifiec on the command line Drydock looks for command_action or
|
|
475
|
+
# action_command methods in the command class.
|
|
476
|
+
#
|
|
477
|
+
# action :E, :eat, "Eat something"
|
|
478
|
+
# command :oysters => Fresh::Oysters
|
|
479
|
+
#
|
|
480
|
+
# # Drydock will look for Fresh::Oysters#eat_oysters and Fresh::Oysters#oysters_eat.
|
|
481
|
+
#
|
|
482
|
+
def action(*args, &b)
|
|
483
|
+
ret = option(*args, &b) # returns an array of all the current option names
|
|
484
|
+
current_command_action << ret.last # the most recent is last
|
|
485
|
+
end
|
|
486
|
+
|
|
364
487
|
# Define a command.
|
|
365
488
|
#
|
|
366
489
|
# command :task do
|
|
@@ -376,15 +499,23 @@ module Drydock
|
|
|
376
499
|
#
|
|
377
500
|
def command(*cmds, &b)
|
|
378
501
|
cmd = cmds.first
|
|
502
|
+
|
|
379
503
|
if cmd.is_a? Hash
|
|
380
|
-
c = cmd.values.first.new(cmd.keys.first, &b)
|
|
504
|
+
c = cmd.values.first.new(cmd.keys.first, &b) # A custom class was specified
|
|
505
|
+
# TODO: handle command [:task, :alias] => Class
|
|
506
|
+
#elsif cmd.is_a? Array
|
|
507
|
+
# p cmd
|
|
381
508
|
else
|
|
382
509
|
c = Drydock::Command.new(cmd, &b)
|
|
383
510
|
end
|
|
384
511
|
|
|
385
512
|
@@command_descriptions[@@command_index] ||= ""
|
|
513
|
+
@@command_actions[@@command_index] ||= []
|
|
514
|
+
@@command_argv_names[@@command_index] ||= []
|
|
386
515
|
|
|
387
516
|
c.desc = @@command_descriptions[@@command_index]
|
|
517
|
+
c.actions = @@command_actions[@@command_index]
|
|
518
|
+
c.argv.fields = @@command_argv_names[@@command_index]
|
|
388
519
|
|
|
389
520
|
# Default Usage Banner.
|
|
390
521
|
# Without this, there's no help displayed for the command.
|
|
@@ -406,8 +537,8 @@ module Drydock
|
|
|
406
537
|
#
|
|
407
538
|
# Either name can be used on the command-line:
|
|
408
539
|
#
|
|
409
|
-
# $
|
|
410
|
-
# $
|
|
540
|
+
# $ yourscript task [options]
|
|
541
|
+
# $ yourscript pointer [options]
|
|
411
542
|
#
|
|
412
543
|
# Inside of the command definition, you have access to the
|
|
413
544
|
# command name that was used via obj.alias.
|
|
@@ -499,20 +630,6 @@ module Drydock
|
|
|
499
630
|
!@@capture.nil?
|
|
500
631
|
end
|
|
501
632
|
|
|
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
633
|
# Returns true if a command with the name +cmd+ has been defined.
|
|
517
634
|
def command?(cmd)
|
|
518
635
|
name = canonize(cmd)
|
|
@@ -649,10 +766,28 @@ module Drydock
|
|
|
649
766
|
(@@command_option_names[@@command_index] ||= [])
|
|
650
767
|
end
|
|
651
768
|
|
|
769
|
+
def current_command_action
|
|
770
|
+
(@@command_actions[@@command_index] ||= [])
|
|
771
|
+
end
|
|
772
|
+
|
|
652
773
|
def get_command_index(cmd)
|
|
653
774
|
@@command_index_map[canonize(cmd)] || -1
|
|
654
775
|
end
|
|
655
776
|
|
|
777
|
+
# Grab the options parser for the current command or create it if it doesn't exist.
|
|
778
|
+
# Returns an instance of OptionParser.
|
|
779
|
+
def get_current_option_parser
|
|
780
|
+
(@@command_opts_parser[@@command_index] ||= OptionParser.new)
|
|
781
|
+
end
|
|
782
|
+
|
|
783
|
+
# Grabs the options parser for the given command.
|
|
784
|
+
# +arg+ can be an index or command name.
|
|
785
|
+
# Returns an instance of OptionParser.
|
|
786
|
+
def get_option_parser(arg)
|
|
787
|
+
index = arg.is_a?(String) ? get_command_index(arg) : arg
|
|
788
|
+
(@@command_opts_parser[index] ||= OptionParser.new)
|
|
789
|
+
end
|
|
790
|
+
|
|
656
791
|
#
|
|
657
792
|
# These are the "reel" defaults
|
|
658
793
|
#
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: delano-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-01 00:00:00 -08: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
|
|
|
@@ -61,6 +61,6 @@ rubyforge_project: drydock
|
|
|
61
61
|
rubygems_version: 1.2.0
|
|
62
62
|
signing_key:
|
|
63
63
|
specification_version: 1
|
|
64
|
-
summary: A seaworthy DSL for writing command line apps
|
|
64
|
+
summary: "Drydock: A seaworthy DSL for writing command line apps"
|
|
65
65
|
test_files: []
|
|
66
66
|
|