lib/rubikon/command.rb in rubikon-0.5.3 vs lib/rubikon/command.rb in rubikon-0.6.0
- old
+ new
@@ -1,12 +1,12 @@
# This code is free software; you can redistribute it and/or modify it under
# the terms of the new BSD License.
#
-# Copyright (c) 2010, Sebastian Staudt
+# Copyright (c) 2010-2011, Sebastian Staudt
require 'rubikon/application/base'
-require 'rubikon/exceptions'
+require 'rubikon/errors'
require 'rubikon/has_arguments'
require 'rubikon/parameter'
module Rubikon
@@ -32,20 +32,18 @@
# the app it belongs to
#
# @param [Application::Base] app The application this command belongs to
# @param [Symbol, #to_sym] name The name of this command, used in application
# arguments
- # @param [Range, Array, Numeric] arg_count The number of arguments this
- # command takes.
+ # @param options (see HasArguments#initialize)
# @param [Proc] block The code block which should be executed by this
# command
# @raise [ArgumentError] if the given application object isn't a Rubikon
# application
# @raise [BlockMissingError] if no command code block is given and a
# command file does not exist
- # @see HasArguments#arg_count=
- def initialize(app, name, arg_count = nil, &block)
+ def initialize(app, name, *options, &block)
super
@params = {}
if block_given?
@@ -56,12 +54,85 @@
code = open(@file_name).read
@block = Proc.new { instance_eval(code) }
end
end
+ # Generate help for this command
+ #
+ # @param [Boolean] show_usage If +true+, the returned String will also
+ # include usage information
+ # @return [String] The contents of the help screen for this command
+ # @since 0.6.0
+ def help(show_usage = true)
+ help = ''
+
+ if show_usage
+ help << " #{name}" if name != :__default
+
+ @params.values.uniq.sort_by {|a| a.name.to_s }.each do |param|
+ help << ' ['
+ ([param.name] + param.aliases).each_with_index do |name, index|
+ name = name.to_s
+ help << '|' if index > 0
+ help << '-' if name.size > 1
+ help << "-#{name}"
+ end
+ help << ' ...' if param.is_a?(Option)
+ help << ']'
+ end
+ end
+
+ help << "\n\n#{description}" unless description.nil?
+
+ help_flags = {}
+ help_options = {}
+ params.each_value do |param|
+ if param.is_a? Flag
+ help_flags[param.name.to_s] = param
+ else
+ help_options[param.name.to_s] = param
+ end
+ end
+
+ param_name = lambda { |name| "#{name.size > 1 ? '-' : ' '}-#{name}" }
+ unless help_flags.empty? && help_options.empty?
+ max_param_length = (help_flags.keys + help_options.keys).
+ max_by { |a| a.size }.size + 2
+ end
+
+ unless help_flags.empty?
+ help << "\n\nFlags:"
+ help_flags.sort_by { |name, param| name }.each do |name, param|
+ help << "\n #{param_name.call(name).ljust(max_param_length)}"
+ help << " #{param.description}" unless param.description.nil?
+ end
+ end
+
+ unless help_options.empty?
+ help << "\n\nOptions:\n"
+ help_options.sort_by { |name, param| name }.each do |name, param|
+ help << " #{param_name.call(name).ljust(max_param_length)} ..."
+ help << " #{param.description}" unless param.description.nil?
+ help << "\n"
+ end
+ end
+
+ help
+ end
+
private
+ # Returns all parameters of this command that are active, i.e. that have
+ # been supplied on the command-line
+ #
+ # @return [Array<Parameter>] All currently active parameters of this
+ # command
+ # @since 0.6.0
+ def active_params
+ @params.values.select { |param| param.active? }
+ end
+
# Add a new parameter for this command
#
# @param [Parameter, Hash] parameter The parameter to add to this
# command. This might also be a Hash where every key will be an
# alias to the corresponding value, e.g. <tt>{ :alias => :parameter
@@ -94,65 +165,29 @@
# If a parameter with the specified method name exists, a call to that
# method will return the value of the parameter.
#
# @param (see ClassMethods#method_missing)
- # @see DSLMethods#params
#
# @example
# option :user, [:who]
# command :hello, [:mood] do
# puts "Hello #{user.who}"
# puts "I feel #{mood}"
# end
def method_missing(name, *args, &block)
- if args.empty? && !block_given? && @params.key?(name)
- @params[name]
- else
- super
- end
- end
-
- # Parses the arguments of this command and sets each Parameter as active
- # if it has been supplied by the user on the command-line. Additional
- # arguments are passed to the individual parameters.
- #
- # @param [Array<String>] args The arguments that have been passed to this
- # command
- # @raise [UnknownParameterError] if an undefined parameter is passed to the
- # command
- # @see Flag
- # @see Option
- def parse_arguments(args)
- current_param = Application::InstanceMethods.
- instance_method(:current_param).bind(@app)
- set_current_param = Application::InstanceMethods.
- instance_method(:current_param=).bind(@app)
-
- @args = []
- args.each do |arg|
- if arg.start_with?('-')
- parameter_name = arg.start_with?('--') ? arg[2..-1] : arg[1..-1]
- parameter = @params[parameter_name.to_sym]
- raise UnknownParameterError.new(arg) if parameter.nil?
- end
-
- unless parameter.nil?
- current_param.call.send(:active!) unless current_param.call.nil?
- set_current_param.call(parameter)
- next
- end
-
- if current_param.call.nil? || !current_param.call.send(:more_args?)
- self << arg
+ if args.empty? && !block_given?
+ if @params.key?(name)
+ return @params[name]
else
- current_param.call.send(:<<, arg)
+ active_params.each do |param|
+ return param.send(name) if param.respond_to_missing?(name)
+ end
end
end
- current_param.call.send(:active!) unless current_param.call.nil?
- set_current_param.call(nil)
+ super
end
# Resets this command to its initial state
#
# @see HasArguments#reset
@@ -170,18 +205,16 @@
# value of a parameter.
#
# @return +true+ if named parameter with the specified name exists
# @see #method_missing
def respond_to_missing?(name, include_private = false)
- @params.key?(name) || super
+ @params.key?(name) ||
+ active_params.any? { |param| param.respond_to_missing?(name) } ||
+ super
end
# Run this command's code block
- #
- # @param [Array<String>] args The arguments that have been passed to this
- # command
- def run(*args)
- parse_arguments(args)
+ def run
check_args
Application::InstanceMethods.instance_method(:sandbox).bind(@app).call.
instance_eval(&@block)
end