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