Module: Drydock
- Extended by:
- Drydock
- Included in:
- Drydock
- Defined in:
- lib/drydock.rb,
lib/drydock.rb,
lib/drydock.rb,
lib/drydock/screen.rb
Overview
Drydock is a DSL for command-line apps. See bin/example for usage examples.
Defined Under Namespace
Modules: Screen Classes: ArgError, Command, FancyArray, InvalidArgument, MissingArgument, NoCommandsDefined, OptError, UnknownCommand
Constant Summary collapse
- VERSION =
0.6
- @@project =
nil
- @@debug =
false
- @@has_run =
false
- @@run =
true
- @@global_opts_parser =
OptionParser.new
- @@global_option_names =
[]
- @@command_opts_parser =
[]
- @@command_option_names =
[]
- @@command_actions =
[]
- @@default_command_with_args =
false
- @@commands =
{}
- @@command_index =
0
- @@command_index_map =
{}
- @@command_argv_names =
an array of names for values of argv
[]
- @@capture =
contains one of :stdout, :stderr
nil
- @@captured =
nil
- @@trawler =
nil
Instance Method Summary collapse
-
#about(txt) ⇒ Object
Provide a description for a command.
-
#action(*args, &b) ⇒ Object
Define a command-specific action.
-
#after(&b) ⇒ Object
Define a block to be called after the command.
-
#alias_command(aliaz, cmd) ⇒ Object
Used to create an alias to a defined command.
-
#argv(*args) ⇒ Object
Provide names for CLI arguments, in the order they appear.
-
#before(&b) ⇒ Object
Define a block to be called before the command.
-
#canonize(cmd) ⇒ Object
Canonizes a string (
cmd
) to the symbol for command names ‘-’ is replaced with ‘_’. - #capture(io) ⇒ Object
- #capture? ⇒ Boolean
-
#capture_io(stream, &block) ⇒ Object
Capture STDOUT or STDERR to prevent it from being printed.
- #captured ⇒ Object
-
#command(*cmds, &b) ⇒ Object
Define a command.
-
#command?(cmd) ⇒ Boolean
Returns true if a command with the name
cmd
has been defined. -
#command_alias(cmd, aliaz) ⇒ Object
Identical to
alias_command
with reversed arguments. -
#command_names ⇒ Object
An array of the currently defined commands names.
-
#commands ⇒ Object
A hash of the currently defined Drydock::Command objects.
-
#debug(toggle = false) ⇒ Object
Enable or disable debug output.
-
#debug? ⇒ Boolean
Returns true if debug output is enabled.
-
#decanonize(cmd) ⇒ Object
Returns a string version of
cmd
, decanonized. -
#default(cmd = nil, with_args = false, &b) ⇒ Object
Define a default command.
-
#default?(cmd) ⇒ Boolean
Is
cmd
the default command?. - #default_with_args? ⇒ Boolean
-
#desc(txt) ⇒ Object
Deprecated.
-
#global_option(*args, &b) ⇒ Object
(also: #global)
Define a global option.
-
#global_usage(msg) ⇒ Object
Define the default global usage banner.
-
#has_run? ⇒ Boolean
Return true if a command has been executed.
-
#ignore(what = :nothing) ⇒ Object
Tell the Drydock parser to ignore something.
-
#option(*args, &b) ⇒ Object
Define a command-specific option.
-
#project(txt = nil) ⇒ Object
The project name.
-
#project? ⇒ Boolean
Has the project been set?.
-
#run!(argv = [], stdin = STDIN) ⇒ Object
Execute the given command.
-
#run=(v) ⇒ Object
Disable automatic execution (enabled by default).
-
#run? ⇒ Boolean
Returns true if automatic execution is enabled.
-
#stdin(&b) ⇒ Object
Define a block for processing STDIN before the command is called.
-
#trawler(cmd) ⇒ Object
The trawler catches any and all unknown commands that pass through Drydock.
-
#trawler? ⇒ Boolean
Has the trawler been set?.
-
#usage(msg) ⇒ Object
Define a command-specific usage banner.
Instance Method Details
#about(txt) ⇒ Object
Provide a description for a command
703 704 705 706 707 |
# File 'lib/drydock.rb', line 703 def about(txt) @@command_descriptions += [txt] return if get_current_option_parser.is_a?(Symbol) get_current_option_parser.on "ABOUT: #{txt}" end |
#action(*args, &b) ⇒ Object
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.
590 591 592 593 |
# File 'lib/drydock.rb', line 590 def action(*args, &b) ret = option(*args, &b) # returns an array of all the current option names current_command_action << ret.last # the most recent is last end |
#after(&b) ⇒ Object
Define a block to be called after the command. This is useful for stopping, closing, etc… the stuff in the before block.
504 505 506 |
# File 'lib/drydock.rb', line 504 def after(&b) @@after_block = b end |
#alias_command(aliaz, cmd) ⇒ Object
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.
664 665 666 667 |
# File 'lib/drydock.rb', line 664 def alias_command(aliaz, cmd) return unless commands.has_key? cmd commands[canonize(aliaz)] = commands[cmd] end |
#argv(*args) ⇒ Object
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
418 419 420 421 |
# File 'lib/drydock.rb', line 418 def argv(*args) @@command_argv_names[@@command_index] ||= [] @@command_argv_names[@@command_index] += args.flatten end |
#before(&b) ⇒ Object
Define a block to be called before the command. This is useful for opening database connections, etc…
498 499 500 |
# File 'lib/drydock.rb', line 498 def before(&b) @@before_block = b end |
#canonize(cmd) ⇒ Object
Canonizes a string (cmd
) to the symbol for command names ‘-’ is replaced with ‘_’
781 782 783 784 785 |
# File 'lib/drydock.rb', line 781 def canonize(cmd) return unless cmd return cmd if cmd.kind_of?(Symbol) cmd.to_s.tr('-', '_').to_sym end |
#capture(io) ⇒ Object
761 762 763 |
# File 'lib/drydock.rb', line 761 def capture(io) @@capture = io end |
#capture? ⇒ Boolean
769 770 771 |
# File 'lib/drydock.rb', line 769 def capture? !@@capture.nil? end |
#capture_io(stream, &block) ⇒ Object
Capture STDOUT or STDERR to prevent it from being printed.
capture(:stdout) do
...
end
800 801 802 803 804 805 806 807 808 809 810 |
# File 'lib/drydock.rb', line 800 def capture_io(stream, &block) raise "We can only capture STDOUT or STDERR" unless stream == :stdout || stream == :stderr begin eval "$#{stream} = StringIO.new" block.call eval("$#{stream}").rewind # Otherwise we'll get nil result = eval("$#{stream}").read ensure eval "$#{stream} = #{stream.to_s.upcase}" # Put it back! end end |
#captured ⇒ Object
765 766 767 |
# File 'lib/drydock.rb', line 765 def captured @@captured end |
#command(*cmds, &b) ⇒ Object
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
608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 |
# File 'lib/drydock.rb', line 608 def command(*cmds, &b) cmd = cmds.shift # Should we accept aliases here? if cmd.is_a? Hash klass = cmd.values.first names = cmd.keys.first if names.is_a? Array cmd, cmds = names.shift, [names].flatten.compact else cmd = names end raise "#{klass} is not a subclass of Drydock::Command" unless klass.ancestors.member?(Drydock::Command) c = klass.new(cmd, &b) # A custom class was specified else c = Drydock::Command.new(cmd, &b) end @@command_descriptions[@@command_index] ||= "" @@command_actions[@@command_index] ||= [] @@command_argv_names[@@command_index] ||= [] c.desc = @@command_descriptions[@@command_index] c.actions = @@command_actions[@@command_index] c.argv.fields = @@command_argv_names[@@command_index] # Default Usage Banner. # Without this, there's no help displayed for the command. option_parser = get_option_parser(@@command_index) if option_parser.is_a?(OptionParser) && option_parser. !~ /^USAGE/ usage "#{c.executable} #{c.cmd}" end @@commands[c.cmd] = c @@command_index_map[c.cmd] = @@command_index @@command_index += 1 # This will point to the next command # Created aliases to the command using any additional command names # i.e. command :something, :sumpin => Something cmds.each { |aliaz| command_alias(cmd, aliaz); } unless cmds.empty? c # Return the Command object end |
#command?(cmd) ⇒ Boolean
Returns true if a command with the name cmd
has been defined.
774 775 776 777 |
# File 'lib/drydock.rb', line 774 def command?(cmd) name = canonize(cmd) @@commands.has_key? name end |
#command_alias(cmd, aliaz) ⇒ Object
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.
672 673 674 675 |
# File 'lib/drydock.rb', line 672 def command_alias(cmd, aliaz) return unless commands.has_key? cmd commands[canonize(aliaz)] = commands[cmd] end |
#command_names ⇒ Object
An array of the currently defined commands names
683 684 685 |
# File 'lib/drydock.rb', line 683 def command_names @@commands.keys.collect { |cmd| decanonize(cmd); } end |
#commands ⇒ Object
A hash of the currently defined Drydock::Command objects
678 679 680 |
# File 'lib/drydock.rb', line 678 def commands @@commands end |
#debug(toggle = false) ⇒ Object
Enable or disable debug output.
debug :on
debug :off
Calling without :on or :off will toggle the value.
395 396 397 398 399 400 401 402 |
# File 'lib/drydock.rb', line 395 def debug(toggle=false) if toggle.is_a? Symbol @@debug = true if toggle == :on @@debug = false if toggle == :off else @@debug = (!@@debug) end end |
#debug? ⇒ Boolean
Returns true if debug output is enabled.
405 406 407 |
# File 'lib/drydock.rb', line 405 def debug? @@debug end |
#decanonize(cmd) ⇒ Object
Returns a string version of cmd
, decanonized. Lowercase, ‘_’ is replaced with ‘-’
789 790 791 792 |
# File 'lib/drydock.rb', line 789 def decanonize(cmd) return unless cmd cmd.to_s.tr('_', '-') end |
#default(cmd = nil, with_args = false, &b) ⇒ Object
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.
465 466 467 468 469 470 471 472 |
# File 'lib/drydock.rb', line 465 def default(cmd=nil, with_args=false, &b) raise "Calling default requires a command name or a block" unless cmd || b # Creates the command and returns the name or just stores given name @@default_command = (b) ? command(cmd || :default, &b).cmd : canonize(cmd) # IDEA: refactor out the argument parser to support different types of CLI @@default_command_with_args = with_args ? true : false @@default_command end |
#default?(cmd) ⇒ Boolean
Is cmd
the default command?
475 476 477 478 |
# File 'lib/drydock.rb', line 475 def default?(cmd) return false if @@default_command.nil? (@@default_command == canonize(cmd)) end |
#default_with_args? ⇒ Boolean
481 |
# File 'lib/drydock.rb', line 481 def default_with_args?; @@default_command_with_args; end |
#desc(txt) ⇒ Object
Deprecated. Use about.
709 710 711 712 |
# File 'lib/drydock.rb', line 709 def desc(txt) STDERR.puts "'desc' is deprecated. Please use 'about' instead." about(txt) end |
#global_option(*args, &b) ⇒ Object Also known as: global
Define a global option. See option
for more info.
536 537 538 539 |
# File 'lib/drydock.rb', line 536 def global_option(*args, &b) args.unshift(@@global_opts_parser) @@global_option_names << option_parser(args, &b) end |
#global_usage(msg) ⇒ Object
Define the default global usage banner. This is displayed with “script -h”.
510 511 512 |
# File 'lib/drydock.rb', line 510 def global_usage(msg) @@global_opts_parser. = "USAGE: #{msg}" end |
#has_run? ⇒ Boolean
Return true if a command has been executed.
727 728 729 |
# File 'lib/drydock.rb', line 727 def has_run? @@has_run end |
#ignore(what = :nothing) ⇒ Object
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.
531 532 533 |
# File 'lib/drydock.rb', line 531 def ignore(what=:nothing) @@command_opts_parser[@@command_index] = :ignore if what == :options || what == :all end |
#option(*args, &b) ⇒ Object
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..tasty # => true
obj..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.
573 574 575 576 |
# File 'lib/drydock.rb', line 573 def option(*args, &b) args.unshift(get_current_option_parser) current_command_option_names << option_parser(args, &b) end |
#project(txt = nil) ⇒ Object
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.
426 427 428 429 430 431 432 433 434 435 436 437 |
# File 'lib/drydock.rb', line 426 def project(txt=nil) return @@project unless txt #begin # require txt.downcase #rescue LoadError => ex # Drydock.run = false # Prevent execution at_exit # abort "Problem during require: #{ex.message}" #end @@project = txt end |
#project? ⇒ Boolean
Has the project been set?
440 441 442 |
# File 'lib/drydock.rb', line 440 def project? (defined?(@@project) && !@@project.nil?) end |
#run!(argv = [], stdin = STDIN) ⇒ Object
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.
735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 |
# File 'lib/drydock.rb', line 735 def run!(argv=[], stdin=STDIN) return if has_run? @@has_run = true raise NoCommandsDefined.new if commands.empty? , cmd_name, , argv = process_arguments(argv) stdin = (defined? @@stdin_block) ? @@stdin_block.call(stdin, []) : stdin command_obj = get_command(cmd_name) command_obj.prepare(cmd_name, argv, stdin, , ) # Execute before block @@before_block.call(command_obj) if defined? @@before_block # Execute the requested command. We'll capture STDERR or STDOUT if desired. @@captured = capture? ? capture_io(@@capture) { command_obj.call } : command_obj.call # Execute after block @@after_block.call(command_obj) if defined? @@after_block rescue OptionParser::InvalidOption => ex raise Drydock::InvalidArgument.new(ex.args) rescue OptionParser::MissingArgument => ex raise Drydock::MissingArgument.new(ex.args) end |
#run=(v) ⇒ Object
Disable automatic execution (enabled by default)
Drydock.run = false
722 723 724 |
# File 'lib/drydock.rb', line 722 def run=(v) @@run = (v.is_a?(TrueClass)) ? true : false end |
#run? ⇒ Boolean
Returns true if automatic execution is enabled.
715 716 717 |
# File 'lib/drydock.rb', line 715 def run? @@run && has_run? == false end |
#stdin(&b) ⇒ Object
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.
492 493 494 |
# File 'lib/drydock.rb', line 492 def stdin(&b) @@stdin_block = b end |
#trawler(cmd) ⇒ Object
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
693 694 695 |
# File 'lib/drydock.rb', line 693 def trawler(cmd) @@trawler = cmd end |
#trawler? ⇒ Boolean
Has the trawler been set?
698 699 700 |
# File 'lib/drydock.rb', line 698 def trawler? !@@trawler.nil? && !@@trawler.to_s.empty? end |
#usage(msg) ⇒ Object
Define a command-specific usage banner. This is displayed with “script command -h”
516 517 518 519 520 521 |
# File 'lib/drydock.rb', line 516 def usage(msg) # The default value given by OptionParser starts with "Usage". That's how # we know we can clear it. get_current_option_parser. = "" if get_current_option_parser. =~ /^Usage:/ get_current_option_parser. << "USAGE: #{msg}" << $/ end |