lib/gli.rb in gli-1.2.6 vs lib/gli.rb in gli-1.3.0

- old
+ new

@@ -1,11 +1,12 @@ -require 'gli/command_line_token.rb' require 'gli/command.rb' -require 'gli/switch.rb' +require 'gli/command_line_token.rb' +require 'gli/copy_options_to_aliases.rb' +require 'gli/exceptions.rb' require 'gli/flag.rb' require 'gli/options.rb' -require 'gli/exceptions.rb' +require 'gli/switch.rb' require 'gli_version.rb' require 'support/help.rb' require 'support/rdoc.rb' require 'support/initconfig.rb' require 'etc' @@ -13,19 +14,23 @@ # A means to define and parse a command line interface that works as # Git's does, in that you specify global options, a command name, command # specific options, and then command arguments. module GLI extend self + include CopyOptionsToAliases @@program_name = $0.split(/\//)[-1] @@post_block = nil @@pre_block = nil @@error_block = nil @@config_file = nil @@use_openstruct = false @@version = nil @@stderr = $stderr + @@program_desc = nil + @@skips_pre = false + @@skips_post = false # Override the device of stderr; exposed only for testing def error_device=(e) #:nodoc: @@stderr = e end @@ -36,19 +41,45 @@ flags.clear commands.clear @@version = nil @@config_file = nil @@use_openstruct = false + @@prog_desc = nil clear_nexts end # Describe the next switch, flag, or command. This should be a # short, one-line description # # +description+:: A String of the short descripiton of the switch, flag, or command following def desc(description); @@next_desc = description; end + # Describe the overall application/programm. This should be a one-sentence summary + # of what your program does that will appear in the help output. + # + # +description+:: A String of the short description of your program's purpose + def program_desc(description=nil) + if description + @@program_desc = description + end + @@program_desc + end + + # Use this if the following command should not have the pre block executed. + # By default, the pre block is executed before each command and can result in + # aborting the call. Using this will avoid that behavior for the following command + def skips_pre + @@skips_pre = true + end + + # Use this if the following command should not have the post block executed. + # By default, the post block is executed after each command. + # Using this will avoid that behavior for the following command + def skips_post + @@skips_post = true + end + # Provide a longer, more detailed description. This # will be reformatted and wrapped to fit in the terminal's columns # # +long_desc+:: A String that is s longer description of the switch, flag, or command following. def long_desc(long_desc); @@next_long_desc = long_desc; end @@ -125,11 +156,11 @@ # You then may call methods on this object to define aspects of that Command. # # +names+:: a String or Symbol, or an Array of String or Symbol that represent all the different names and aliases for this command. # def command(*names) - command = Command.new([names].flatten,@@next_desc,@@next_arg_name,@@next_long_desc) + command = Command.new([names].flatten,@@next_desc,@@next_arg_name,@@next_long_desc,@@skips_pre,@@skips_post) commands[command.name] = command yield command clear_nexts end @@ -191,38 +222,42 @@ commands[:rdoc] = rdoc if !commands[:rdoc] commands[:help] = DefaultHelpCommand.new(@@version,rdoc) if !commands[:help] exit_code = 0 begin config = parse_config - global_options,command,options,arguments = parse_options(args,config) + override_defaults_based_on_config(config) + global_options,command,options,arguments = parse_options(args) copy_options_to_aliased_versions(global_options,command,options) global_options = convert_to_openstruct?(global_options) options = convert_to_openstruct?(options) if proceed?(global_options,command,options,arguments) command = commands[:help] if !command command.execute(global_options,options,arguments) - @@post_block.call(global_options,command,options,arguments) if @@post_block + if !command.skips_post && @@post_block + @@post_block.call(global_options,command,options,arguments) + end end rescue Exception => ex @@stderr.puts error_message(ex) if regular_error_handling?(ex) - raise ex if ENV['GLI_DEBUG'] == 'true' - exit_code = if ex.respond_to? :exit_code ex.exit_code else -2 end + raise ex if ENV['GLI_DEBUG'] == 'true' end exit_code end # True if we should proceed with executing the command; this calls # the pre block if it's defined def proceed?(global_options,command,options,arguments) #:nodoc: - if @@pre_block + if command && command.skips_pre + true + elsif @@pre_block @@pre_block.call(global_options,command,options,arguments) else true end end @@ -286,78 +321,53 @@ # Copies all options in both global_options and options to keys for the aliases of those flags. # For example, if a flag works with either -f or --flag, this will copy the value from [:f] to [:flag] # to allow the user to access the options by any alias def copy_options_to_aliased_versions(global_options,command,options) # :nodoc: - copy_options_to_aliases(global_options,self) - copy_options_to_aliases(options,command) + copy_options_to_aliases(global_options) + command.copy_options_to_aliases(options) end - # For each option in options, copies its value to keys for the aliases of the flags or - # switches in gli_like - # - # options - Hash of options parsed from command line; this is an I/O param - # gli_like - Object resonding to flags and switches in the same way that GLI or a Command instance do - def copy_options_to_aliases(options,gli_like) # :nodoc: - new_options = {} - options.each do |key,value| - if gli_like.flags[key] && gli_like.flags[key].aliases - gli_like.flags[key].aliases.each do |alias_name| - new_options[alias_name] = value - end - elsif gli_like.switches[key] && gli_like.switches[key].aliases - gli_like.switches[key].aliases.each do |alias_name| - new_options[alias_name] = value - end - end - end - options.merge!(new_options) - end - def parse_config # :nodoc: return nil if @@config_file.nil? require 'yaml' if File.exist?(@@config_file) - File.open(@@config_file) { |f| YAML::load(f) } + File.open(@@config_file) { |file| YAML::load(file) } else {} end end # Returns an array of four values: # * global options (as a Hash) # * Command # * command options (as a Hash) # * arguments (as an Array) - def parse_options(args,config=nil) # :nodoc: - command_configs = {} - if config.nil? - config = {} - else - command_configs = config.delete(GLI::InitConfig::COMMANDS_KEY) if !config.nil? - end - global_options,command,options,arguments = parse_options_helper(args.clone,config,nil,Hash.new,Array.new,command_configs) + def parse_options(args) # :nodoc: + global_options,command,options,arguments = parse_options_helper(args.clone,Hash.new,nil,Hash.new,Array.new) flags.each { |name,flag| global_options[name] = flag.default_value if !global_options[name] } command.flags.each { |name,flag| options[name] = flag.default_value if !options[name] } return [global_options,command,options,arguments] end # Finds the index of the first non-flag # argument or -1 if there wasn't one. def find_non_flag_index(args) # :nodoc: - args.each_index do |i| - return i if args[i] =~ /^[^\-]/; - return i-1 if args[i] =~ /^\-\-$/; + args.each_with_index do |item,index| + return index if item =~ /^[^\-]/ + return index-1 if item =~ /^\-\-$/ end - -1; + -1 end def clear_nexts # :nodoc: @@next_desc = nil @@next_arg_name = nil @@next_default_value = nil @@next_long_desc = nil + @@skips_pre = false + @@skips_post = false end clear_nexts def flags # :nodoc: @@ -374,11 +384,10 @@ # <code>args</code>:: the arguments that have yet to be processed # <code>global_options</code>:: the global options hash # <code>command</code>:: the Command that has been identified (or nil if not identified yet) # <code>command_options</code>:: options for Command # <code>arguments</code>:: the arguments for Command - # <code>command_configs</code>:: the configuration file for all commands, used as defaults # # This works by finding the first non-switch/flag argument, and taking that sublist and trying to pick out # flags and switches. After this is done, one of the following is true: # * the sublist is empty - in this case, go again, as there might be more flags to parse # * the sublist has a flag left in it - unknown flag; we bail @@ -387,11 +396,11 @@ # This sort of does the same thing in two phases; in the first phase, the command hasn't been identified, so # we are looking for global switches and flags, ending when we get the command. # # Once the command has been found, we start looking for command-specific flags and switches. # When those have been found, we know the rest of the argument list is arguments for the command - def parse_options_helper(args,global_options,command,command_options,arguments,command_configs) # :nodoc: + def parse_options_helper(args,global_options,command,command_options,arguments) # :nodoc: non_flag_i = find_non_flag_index(args) all_flags = false if non_flag_i == 0 # no flags if !command @@ -399,13 +408,12 @@ command = find_command(command_name) raise UnknownCommand.new("Unknown command '#{command_name}'") if !command return parse_options_helper(args, global_options, command, - default_command_options(command,command_configs), - arguments, - command_configs) + Hash.new, + arguments) else return global_options,command,command_options,arguments + args end elsif non_flag_i == -1 all_flags = true @@ -451,11 +459,11 @@ if try_me.empty? return [global_options,command,command_options,arguments] if rest.empty? # If we have no more options we've parsed them all # and rest may have more - return parse_options_helper(rest,global_options,command,command_options,arguments,command_configs) + return parse_options_helper(rest,global_options,command,command_options,arguments) else if command check = rest check = rest + try_me if all_flags check.each() do |arg| @@ -475,27 +483,21 @@ raise UnknownCommand.new("Unknown command '#{command_name}'") if !command return parse_options_helper(rest, global_options, command, - default_command_options(command,command_configs), - arguments, - command_configs) + Hash.new, + arguments) end end end - def default_command_options(command,command_configs) # :nodoc: - options = (command_configs && command_configs[command.name.to_sym]) || {} - end - def find_command(name) # :nodoc: sym = name.to_sym return commands[name.to_sym] if commands[sym] - commands.keys.each do |command_name| - command = commands[command_name] + commands.each do |command_name,command| return command if (command.aliases && command.aliases.include?(sym)) end nil end @@ -515,6 +517,29 @@ if one_option.aliases raise ArgumentError.new("#{name} has already been specified as an alias of #{type} #{one_option_name} #{context}") if one_option.aliases.include? name end end end + + # Sets the default values for flags based on the configuration + def override_defaults_based_on_config(config) + config ||= {} + config['commands'] ||= {} + + override_default(flags,config) + override_default(switches,config) + + commands.each do |command_name,command| + command_config = config['commands'][command_name] || {} + + override_default(command.flags,command_config) + override_default(command.switches,command_config) + end + end + + def override_default(tokens,config) + tokens.each do |name,token| + token.default_value=config[name] if config[name] + end + end + end