Module Drydock

  1. lib/drydock.rb

Drydock is a DSL for command-line apps. See bin/example for usage examples.

Constants

VERSION = 0.6

Public instance methods

about (txt)

Provide a description for a command

[show source]
     # File lib/drydock.rb, line 707
707:   def about(txt)
708:     @@command_descriptions += [txt]
709:     return if get_current_option_parser.is_a?(Symbol)
710:     get_current_option_parser.on "ABOUT: #{txt}"
711:   end
action (*args, &b)

Define a command-specific action.

This is functionally very similar to option, but with an exciting and buoyant twist: Drydock keeps track of actions for each command (in addition to treating it like an option). When an action is specified on the command line Drydock looks for command_action or action_command methods in the command class.

action :E, :eat, "Eat something"
command :oysters => Fresh::Oysters

# Drydock will look for Fresh::Oysters#eat_oysters and Fresh::Oysters#oysters_eat.
[show source]
     # File lib/drydock.rb, line 594
594:   def action(*args, &b)
595:     ret = option(*args, &b) # returns an array of all the current option names
596:     current_command_action << ret.last # the most recent is last
597:   end
after (&b)

Define a block to be called after the command. This is useful for stopping, closing, etc... the stuff in the before block.

[show source]
     # File lib/drydock.rb, line 508
508:   def after(&b)
509:     @@after_block = b
510:   end
alias_command (aliaz, cmd)

Used to create an alias to a defined command. Here’s an example:

command :task do; ...; end
alias_command :pointer, :task

Either name can be used on the command-line:

$ yourscript task [options]
$ yourscript pointer [options]

Inside of the command definition, you have access to the command name that was used via obj.alias.

[show source]
     # File lib/drydock.rb, line 668
668:   def alias_command(aliaz, cmd)
669:     return unless commands.has_key? cmd
670:     commands[canonize(aliaz)] = commands[cmd]
671:   end
argv (*args)

Provide names for CLI arguments, in the order they appear.

$ yourscript sample malpeque zinqy
argv :name, :flavour
command :sample do |obj|
  obj.argv.name        # => malpeque
  obj.argv.flavour     # => zinqy
end
[show source]
     # File lib/drydock.rb, line 422
422:   def argv(*args)
423:     @@command_argv_names[@@command_index] ||= []
424:     @@command_argv_names[@@command_index] += args.flatten
425:   end
before (&b)

Define a block to be called before the command. This is useful for opening database connections, etc...

[show source]
     # File lib/drydock.rb, line 502
502:   def before(&b)
503:     @@before_block = b
504:   end
canonize (cmd)

Canonizes a string (cmd) to the symbol for command names ’-’ is replaced with ‘_’

[show source]
     # File lib/drydock.rb, line 785
785:   def canonize(cmd)
786:     return unless cmd
787:     return cmd if cmd.kind_of?(Symbol)
788:     cmd.to_s.tr('-', '_').to_sym
789:   end
capture (io)
[show source]
     # File lib/drydock.rb, line 765
765:   def capture(io)
766:     @@capture = io
767:   end
capture? ()
[show source]
     # File lib/drydock.rb, line 773
773:   def capture?
774:     !@@capture.nil?
775:   end
capture_io (stream, &block)

Capture STDOUT or STDERR to prevent it from being printed.

capture(:stdout) do
  ...
end
[show source]
     # File lib/drydock.rb, line 804
804:   def capture_io(stream, &block)
805:     raise "We can only capture STDOUT or STDERR" unless stream == :stdout || stream == :stderr
806:     begin
807:       eval "$#{stream} = StringIO.new"
808:       block.call
809:       eval("$#{stream}").rewind                  # Otherwise we'll get nil 
810:       result = eval("$#{stream}").read
811:     ensure
812:       eval "$#{stream} = #{stream.to_s.upcase}"  # Put it back!
813:     end
814:   end
captured ()
[show source]
     # File lib/drydock.rb, line 769
769:   def captured
770:     @@captured
771:   end
command (*cmds, &b)

Define a command.

command :task do
  ...
end

A custom command class can be specified using Hash syntax. The class must inherit from Drydock::Command (class CustomeClass < Drydock::Command)

command :task => CustomCommand do
  ...
end
[show source]
     # File lib/drydock.rb, line 612
612:   def command(*cmds, &b)
613:     cmd = cmds.shift # Should we accept aliases here?
614:     
615:     if cmd.is_a? Hash
616:       klass = cmd.values.first
617:       names = cmd.keys.first
618:       if names.is_a? Array
619:         cmd, cmds = names.shift, [names].flatten.compact
620:       else
621:         cmd = names
622:       end
623:       raise "#{klass} is not a subclass of Drydock::Command" unless klass.ancestors.member?(Drydock::Command)
624:       c = klass.new(cmd, &b)          # A custom class was specified
625:     else
626:       c = Drydock::Command.new(cmd, &b)
627:     end
628:     
629:     @@command_descriptions[@@command_index] ||= ""
630:     @@command_actions[@@command_index] ||= []
631:     @@command_argv_names[@@command_index] ||= []
632:     
633:     c.desc = @@command_descriptions[@@command_index]
634:     c.actions = @@command_actions[@@command_index]
635:     c.argv.fields = @@command_argv_names[@@command_index]
636:     
637:     # Default Usage Banner. 
638:     # Without this, there's no help displayed for the command. 
639:     option_parser = get_option_parser(@@command_index)
640:     if option_parser.is_a?(OptionParser) && option_parser.banner !~ /^USAGE/
641:       usage "#{c.executable} #{c.cmd}"
642:     end
643:     
644:     @@commands[c.cmd] = c
645:     @@command_index_map[c.cmd] = @@command_index
646:     @@command_index += 1 # This will point to the next command
647:     
648:     # Created aliases to the command using any additional command names 
649:     # i.e. command :something, :sumpin => Something
650:     cmds.each { |aliaz| command_alias(cmd, aliaz); } unless cmds.empty?
651:     
652:     c  # Return the Command object
653:   end
command? (cmd)

Returns true if a command with the name cmd has been defined.

[show source]
     # File lib/drydock.rb, line 778
778:   def command?(cmd)
779:     name = canonize(cmd)
780:     @@commands.has_key? name
781:   end
command_alias (cmd, aliaz)

Identical to alias_command with reversed arguments. For whatever reason I forget the order so Drydock supports both. Tip: the argument order matches the method name.

[show source]
     # File lib/drydock.rb, line 676
676:   def command_alias(cmd, aliaz)
677:     return unless commands.has_key? cmd
678:     commands[canonize(aliaz)] = commands[cmd]
679:   end
command_names ()

An array of the currently defined commands names

[show source]
     # File lib/drydock.rb, line 687
687:   def command_names
688:     @@commands.keys.collect { |cmd| decanonize(cmd); }
689:   end
commands ()

A hash of the currently defined Drydock::Command objects

[show source]
     # File lib/drydock.rb, line 682
682:   def commands
683:     @@commands
684:   end
debug (toggle=false)

Enable or disable debug output.

debug :on
debug :off

Calling without :on or :off will toggle the value.

[show source]
     # File lib/drydock.rb, line 399
399:   def debug(toggle=false)
400:     if toggle.is_a? Symbol
401:       @@debug = true if toggle == :on
402:       @@debug = false if toggle == :off
403:     else
404:       @@debug = (!@@debug)
405:     end
406:   end
debug? ()

Returns true if debug output is enabled.

[show source]
     # File lib/drydock.rb, line 409
409:   def debug?
410:     @@debug
411:   end
decanonize (cmd)

Returns a string version of cmd, decanonized. Lowercase, ‘_’ is replaced with ’-’

[show source]
     # File lib/drydock.rb, line 793
793:   def decanonize(cmd)
794:     return unless cmd
795:     cmd.to_s.tr('_', '-')
796:   end
default (cmd=nil, with_args=false, &b)

Define a default command. You can specify a command name that has been or will be defined in your script:

default :task

Or you can supply a block which will be used as the default command:

default do |obj|            # This command will be named "default"
  # ...
end

default :hullinspector do   # This one will be named "hullinspector"
  # ...
end

If with_args is specified, the default command will receive all unknown values as arguments. This is necessary to define explicitly because drydock parses arguments expecting a command name. If the default command accepts arguments and with_args is not specified, drydock will raise an unknown command exception for the first argument.

[show source]
     # File lib/drydock.rb, line 469
469:   def default(cmd=nil, with_args=false, &b)
470:     raise "Calling default requires a command name or a block" unless cmd || b
471:     # Creates the command and returns the name or just stores given name
472:     @@default_command = (b) ? command(cmd || :default, &b).cmd : canonize(cmd)
473:     # IDEA: refactor out the argument parser to support different types of CLI
474:     @@default_command_with_args = with_args ? true : false
475:     @@default_command
476:   end
default? (cmd)

Is cmd the default command?

[show source]
     # File lib/drydock.rb, line 479
479:   def default?(cmd)
480:     return false if @@default_command.nil?
481:     (@@default_command == canonize(cmd))
482:   end
default_with_args? ()
[show source]
     # File lib/drydock.rb, line 485
485:   def default_with_args?; @@default_command_with_args; end
desc (txt)

Deprecated. Use about.

[show source]
     # File lib/drydock.rb, line 713
713:   def desc(txt)
714:     STDERR.puts "'desc' is deprecated. Please use 'about' instead."
715:     about(txt) 
716:   end
global (*args, &b)

Alias for global_option

global_option (*args, &b)

Define a global option. See option for more info.

[show source]
     # File lib/drydock.rb, line 540
540:   def global_option(*args, &b)
541:     args.unshift(@@global_opts_parser)
542:     @@global_option_names << option_parser(args, &b)
543:   end
global_usage (msg)

Define the default global usage banner. This is displayed with “script -h”.

[show source]
     # File lib/drydock.rb, line 514
514:   def global_usage(msg)
515:     @@global_opts_parser.banner = "USAGE: #{msg}"
516:   end
has_run? ()

Return true if a command has been executed.

[show source]
     # File lib/drydock.rb, line 731
731:   def has_run?
732:     @@has_run
733:   end
ignore (what=:nothing)

Tell the Drydock parser to ignore something. Drydock will currently only listen to you if you tell it to “ignore :options”, otherwise it will ignore you!

what the thing to ignore. When it equals :options Drydock will not parse the command-specific arguments. It will pass the arguments directly to the Command object. This is useful when you want to parse the arguments in some a way that’s too crazy, dangerous for Drydock to handle automatically.

[show source]
     # File lib/drydock.rb, line 535
535:   def ignore(what=:nothing)
536:     @@command_opts_parser[@@command_index] = :ignore if what == :options || what == :all
537:   end
option (*args, &b)

Define a command-specific option.

args is passed directly to OptionParser.on so it can contain anything that’s valid to that method. If a class is included, it will tell OptionParser to expect a value otherwise it assumes a boolean value. Some examples:

option :h, :help, "Displays this message"
option '-l x,y,z', '--lang=x,y,z', Array, "Requested languages"

You can also supply a block to fiddle with the values. The final
value becomes the option's value:

option :m, :max, Integer, "Maximum threshold" do |v|
  v = 100 if v > 100
  v
end

All calls to option must come before the command they’re associated to. Example:

option :t, :tasty,          "A boolean switch"
option     :reason, String, "Requires a parameter"
command :task do |obj|;
  obj.options.tasty       # => true
  obj.options.reason      # => I made the sandwich!
end

When calling your script with a specific command-line option, the value is available via obj.longname inside the command block.

[show source]
     # File lib/drydock.rb, line 577
577:   def option(*args, &b)
578:     args.unshift(get_current_option_parser)
579:     current_command_option_names << option_parser(args, &b)
580:   end
project (txt=nil)

The project name. This is currently only used when printing list of commands (see: Drydock::Command#show_commands). It may be used elsewhere in the future.

[show source]
     # File lib/drydock.rb, line 430
430:   def project(txt=nil)
431:     
432:     return @@project unless txt
433:     
434:     #begin
435:     #  require txt.downcase
436:     #rescue LoadError => ex
437:     #  Drydock.run = false  # Prevent execution at_exit
438:     #  abort "Problem during require: #{ex.message}"
439:     #end
440:     @@project = txt
441:   end
project? ()

Has the project been set?

[show source]
     # File lib/drydock.rb, line 444
444:   def project?
445:     (defined?(@@project) && !@@project.nil?)
446:   end
run! (argv=[], stdin=STDIN)

Execute the given command. By default, Drydock automatically executes itself and provides handlers for known errors. You can override this functionality by calling Drydock.run! yourself. Drydock will only call run! once.

[show source]
     # File lib/drydock.rb, line 739
739:   def run!(argv=[], stdin=STDIN)
740:     return if has_run?
741:     @@has_run = true
742:     raise NoCommandsDefined.new if commands.empty?
743:     
744:     global_options, cmd_name, command_options, argv = process_arguments(argv)
745:     stdin = (defined? @@stdin_block) ? @@stdin_block.call(stdin, []) : stdin
746:     
747:     command_obj = get_command(cmd_name)
748:     command_obj.prepare(cmd_name, argv, stdin, global_options, command_options)
749:     
750:     # Execute before block
751:     @@before_block.call(command_obj) if defined? @@before_block
752:     
753:     # Execute the requested command. We'll capture STDERR or STDOUT if desired. 
754:     @@captured = capture? ? capture_io(@@capture) { command_obj.call } : command_obj.call
755:         
756:     # Execute after block
757:     @@after_block.call(command_obj) if defined? @@after_block
758:     
759:   rescue OptionParser::InvalidOption => ex
760:     raise Drydock::InvalidArgument.new(ex.args)
761:   rescue OptionParser::MissingArgument => ex
762:     raise Drydock::MissingArgument.new(ex.args)
763:   end
run= (v)

Disable automatic execution (enabled by default)

Drydock.run = false
[show source]
     # File lib/drydock.rb, line 726
726:   def run=(v)
727:     @@run = (v.is_a?(TrueClass)) ? true : false 
728:   end
run? ()

Returns true if automatic execution is enabled.

[show source]
     # File lib/drydock.rb, line 719
719:   def run?
720:     @@run && has_run? == false
721:   end
stdin (&b)

Define a block for processing STDIN before the command is called. The command block receives the return value of this block as obj.stdin:

command :task do |obj|;
  obj.stdin   # => ...
end

If a stdin block isn’t defined, stdin above will be the STDIN IO handle.

[show source]
     # File lib/drydock.rb, line 496
496:   def stdin(&b)
497:     @@stdin_block = b
498:   end
trawler (cmd)

The trawler catches any and all unknown commands that pass through Drydock. It’s like the captain of aliases. cmd is the name of the command to direct unknowns to.

trawler :command_name
[show source]
     # File lib/drydock.rb, line 697
697:   def trawler(cmd)
698:     @@trawler = cmd
699:   end
trawler? ()

Has the trawler been set?

[show source]
     # File lib/drydock.rb, line 702
702:   def trawler?
703:     !@@trawler.nil? && !@@trawler.to_s.empty?
704:   end
usage (msg)

Define a command-specific usage banner. This is displayed with “script command -h“

[show source]
     # File lib/drydock.rb, line 520
520:   def usage(msg)
521:     # The default value given by OptionParser starts with "Usage". That's how
522:     # we know we can clear it. 
523:     get_current_option_parser.banner = "" if get_current_option_parser.banner =~ /^Usage:/
524:     get_current_option_parser.banner << "USAGE: #{msg}" << $/
525:   end