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