lib/executable.rb in executable-1.1.0 vs lib/executable.rb in executable-1.2.0

- old
+ new

@@ -1,143 +1,82 @@ -# = Executable Mixin +# encoding: utf-8 + +require 'executable/errors' +require 'executable/parser' +require 'executable/help' +require 'executable/utils' +require 'executable/domain' +require 'executable/dispatch' + +# Executable is a mixin for creating robust, inheritable and +# reusable command line interfaces. # -# The Executable mixin is a very quick and and easy way to make almost -# any class usable via a command line interface. It simply uses writer -# methods as option setters, and the first command line argument as a -# method to call with the subsequent arguments passed to the method. -# -# The only limitation of this approach is that non-boolean options must -# be specified with `key=value` notation. -# -# class Example -# include Executable -# -# attr_accessor :quiet -# -# def bread(*args) -# ["bread", quiet, *args] -# end -# -# def butter(*args) -# ["butter", quiet, *args] -# end -# end -# -# ex = Example.new -# -# ex.execute!("butter yum") -# => ["butter", nil, "yum"] -# -# ex.execute!("bread --quiet") -# => ["butter", true] -# -# Executable also provides #option_missing, which you can overriden to provide -# suitable results when a given command line option has no corresponding -# writer method. -# module Executable - # Used the #excute! method to invoke the command. - def execute!(argv=ARGV) - Executable.execute(self, argv) + # + # When Exectuable is included into a class, the class is + # also extended by `Executable::Doamin`. + # + def self.included(base) + base.extend Domain end - ## When no attribute write exists for an option that has been given on - ## the command line #option_missing is called. Override #option_missing - ## to handle these cases, if needed. Otherwise a NoMethodArgument will be - ## raised. This callback method receives the name and value of the option. - #def option_missing(opt, arg) - # raise NoMethodError, "undefined option `#{opt}=' for #{self}" - #end - - class << self - - # Process the arguments as an exectuable against the given object. - def execute(obj, argv=ARGV) - args = parse(obj, argv) - subcmd = args.first - if subcmd && !obj.respond_to?("#{subcmd}=") - obj.send(*args) - else - obj.method_missing(*args) - end + # + # Default initializer, simply takes a hash of settings + # to set attributes via writer methods. Not existnt + # attributes are simply ignored. + # + def initialize(settings={}) + settings.each do |k,v| + __send__("#{k}=", v) if respond_to?("#{k}=") end + end - # The original name for #execute. - alias_method :run, :execute +public - # Parse command line with respect to +obj+. - def parse(obj, argv) - case argv - when String - require 'shellwords' - argv = Shellwords.shellwords(argv) - else - argv = argv.dup - end + # + # Command invocation abstract method. + # + def call(*args) + #puts cli.show_help # TODO: show help instead of error ? + raise NotImplementedError + end - argv = argv.dup - args, opts, i = [], {}, 0 - while argv.size > 0 - case opt = argv.shift - when /=/ - parse_equal(obj, opt, argv) - when /^--/ - parse_option(obj, opt, argv) - when /^-/ - parse_flags(obj, opt, argv) - else - args << opt - end - end - return args - end + # + # Convert Executable to Proc object. + # + def to_proc + lambda { |*args| call(*args) } + end - # Parse a setting option. - def parse_equal(obj, opt, argv) - if md = /^[-]*(.*?)=(.*?)$/.match(opt) - x, v = md[1], md[2] - else - raise ArgumentError, "#{x}" - end - # TODO: to_b if 'true' or 'false' ? - #if obj.respond_to?("#{x}=") - obj.send("#{x}=", v) - #else - # obj.option_missing(x, v) - #end - end + alias_method :inspect, :to_s - # Parse a named boolean option. - def parse_option(obj, opt, argv) - x = opt.sub(/^--/, '') - #if obj.respond_to?("#{x}=") - obj.send("#{x}=", true) - #else - # obj.option_missing(x, true) - #end - end + # + # Output command line help. + # + def to_s + self.class.help.to_s # usage ? + end - # Parse flags. Each character of a flag set is treated as a separate option. - # For example: - # - # $ foo -abc - # - # Would be parsed the same as: - # - # $ foo -a -b -c - # - def parse_flags(obj, opt, args) - x = opt.sub(/^-/, '') - #c = 0 - x.split(//).each do |k| - #if obj.respond_to?("#{k}=") - obj.send("#{k}=", true) - #else - # obj.option_missing(x, true) - #end - end - end + # + # Access to underlying Help instance. + # + def cli + self.class.cli + end - end #class << self + # + # Override option_missing if needed. This receives the name of the option + # and the remaining arguments list. It must consume any arguments it uses + # from the begining of the list (i.e. in-place manipulation). + # + def option_missing(opt, argv) + raise NoOptionError, opt + end + + # Base class alteranative ot using the mixin. + # + class Command + include Executable + end end