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.
Files changed (6) hide show
  1. data/CHANGES.txt +24 -1
  2. data/README.rdoc +34 -34
  3. data/bin/example +33 -18
  4. data/drydock.gemspec +4 -5
  5. data/lib/drydock.rb +205 -70
  6. 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
- * FIX: Bug, "interning empty string" error when bare "option" is used
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 is contained in a single .rb which can be copied directly into your project.
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
- command :welcome do
36
- puts "Welcome to Drydock."
37
- puts "For available commands:"
38
- puts "#{$0} show-commands"
39
- end
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
- 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!
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 Drydock after all.
7
+ # a drydock after all.
8
8
  #
9
- # If you're reading this via the Rdocs you won't see the code. Try:
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, argv|
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, argv|
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
- if argv.empty?
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 process -c -d " " -t 15 http://solutious.com/'
107
- usage 'echo "http://solutious.com/" | ruby bin/example process -c -d " " -t 15'
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
- command :uri do |obj, argv, stdin|
118
- # +cmd+ is the string used to evoke this command. Useful with alias_command (see below).
119
- # +stdin+ is either an IO object or a custom object defined with a stdin block (see below)
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 = [stdin, argv].flatten # Combine the argv and stdin arrays
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 # The default code when :check is false
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.4.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.date = %q{2008-08-17}
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
- # i.e. "example -v date -f yaml"
35
+ # i.e. "example -v select --location kumamoto"
11
36
  #
12
37
  # global :v, :verbose, "I want mooooore!"
13
- # option :f, :format, String, "Long date format"
14
- # command :date do |obj|
15
- # puts obj.global.verbose #=> true
16
- # puts obj.option.format #=> "yaml"
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
- # You can inherit from this class to create your own: EatFood < Drydock::Command.
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 => EatFood do |obj|; ...; end
52
+ # command :eat => Malpeque do |obj|
53
+ # # ... do stuff with your obj
54
+ # end
23
55
  #
24
56
  class Command
25
- VERSION = 0.4
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
- # +cmd_str+ is the short name used to evoke this command. It will equal @cmd
69
- # unless an alias was used used to evoke this command.
70
- # +argv+ an array of unnamed arguments. If ignore :options was declared this
71
- # will contain the arguments exactly as they were defined on the command-line.
72
- # +stdin+ contains the output of stdin do; ...; end otherwise it's a STDIN IO handle.
73
- # +global_options+ a hash of the global options specified on the command-line
74
- # +options+ a hash of the command-specific options specific on the command-line.
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
- block_args = [self, argv, stdin]
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
- @b.call(*block_args[0..(@b.arity-1)]) # send only as many args as defined
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
- Drydock.commands.keys.sort{ |a,b| a.to_s <=> b.to_s }.each do |cmd|
112
- msg = Drydock.commands[cmd].desc
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
- msg = "See: #{Drydock.decanonize(Drydock.commands[cmd].cmd)} (this is an alias)"
213
+ (cmds[Drydock.commands[cmd].cmd][:aliases] ||= []) << pretty
214
+ next
117
215
  end
118
-
119
- puts " %16s: %s" % [Drydock.decanonize(cmd), msg]
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 b named "hullinspector"
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 in a named argument:
376
+ # The command block receives the return value of this block as obj.stdin:
270
377
  #
271
- # command :task do |obj, argv, stdin|; ...; end
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
- # $ script task [options]
410
- # $ script pointer [options]
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.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: 2008-08-17 00:00:00 -07:00
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 inspired by Blake Mizerany's Frylock
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