lib/rubikon/application/dsl_methods.rb in rubikon-0.5.3 vs lib/rubikon/application/dsl_methods.rb in rubikon-0.6.0
- old
+ new
@@ -1,10 +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/argument_vector'
+
module Rubikon
module Application
# This module contains all DSL-related instance methods of
@@ -51,35 +53,44 @@
# Call another named command with the given arguments
#
# @param [Symbol] command_name The name of the command to call
# @param [Array<String>] args The arguments to pass to the called command
+ # @see ArgumentVector#params!
# @see Command#run
def call(command_name, *args)
- command = @current_command
+ args.extend ArgumentVector
+ current_command = @current_command
+ current_param = @current_param
+
@current_command = @commands[command_name]
- @current_command.send(:run, *args)
- @current_command = command
+ args.params!(@current_command.params).each do |param|
+ @current_param = param
+ param.send :active!
+ @current_param = nil
+ end
+ @current_command.send :run
+ @current_command = current_command
+ @current_param = current_param
end
# Define a new application Command or an alias to an existing one
#
# @param [String, Hash] name The name of the Command as used in
# application parameters. This might also be a Hash where every
# key will be an alias to the corresponding value, e.g. <tt>{
# :alias => :command }</tt>.
- # @param [String] description A description for this Command for use in
- # the application's help screen
+ # @param options (see HasArguments#initialize)
# @param [Proc] block A block that contains the code that should be
# executed when this Command is called, i.e. when the application
# is called with the associated parameter
#
# @return [Command]
# @see default
# @see Command
# @since 0.2.0
- def command(name, arg_count = nil, description = nil, &block)
+ def command(name, *options, &block)
command = nil
if name.is_a? Hash
name.each do |alias_name, command_name|
command = @commands[command_name]
@@ -89,12 +100,11 @@
command.aliases << alias_name
@commands[alias_name] = command
end
end
else
- command = Command.new(self, name, arg_count, &block)
- command.description = description unless description.nil?
+ command = Command.new(self, name, *options, &block)
@commands.each do |command_alias, command_name|
if command_name == command.name
@commands[command_alias] = command
command.aliases << command_alias
end
@@ -121,28 +131,69 @@
end
# Define the default Command of the application, i.e. the Command that is
# called if no matching Command parameter can be found
#
- # @param [String] description A description for this Command for use in
- # the application's help screen
+ # @param options (see HasArguments#initialize)
# @param [Proc] block A block that contains the code that should be
# executed when this Command is called, i.e. when no command
# parameter is given to the application
#
# @return [Command] The default Command object
# @see Command
# @see command
# @since 0.2.0
- def default(arg_count = nil, description = nil, &block)
- if arg_count.is_a? Symbol
- command({ :__default => arg_count })
+ #
+ # @example Define a default command with an argument
+ # default 'This is the default', :arg do
+ # ...
+ # end
+ #
+ # @example Use another command as default
+ # default :other_command
+ def default(*options, &block)
+ if options.size == 1 && options.first.is_a?(Symbol) && !block_given?
+ command :__default => options.first
else
- command(:__default, arg_count, description, &block)
+ command :__default, *options, &block
end
end
+ # Set the default configuration for this application
+ #
+ # @param [Hash] config The default configuration to use
+ # @see #config
+ # @since 0.6.0
+ def default_config=(config)
+ unless config.is_a? Hash
+ raise ArgumentError.new('Configuration has to be a Hash')
+ end
+
+ @default_config = config
+ end
+
+ # Output a line of text using +IO#puts+ of the error output stream
+ #
+ # @param [String] text The text to write into the error output stream
+ # @since 0.6.0
+ def error(text = nil)
+ estream.puts text
+ end
+
+ # Convenience method for accessing the user-defined error output stream
+ #
+ # Use this if you want to work directly with the error output stream
+ #
+ # @return [IO] The error output stream object - usually +$stderr+
+ # @since 0.6.0
+ #
+ # @example
+ # estream.flush
+ def estream
+ @settings[:estream]
+ end
+
# Create a new Flag with the given name for the next Command
#
# @param [Symbol, #to_sym] name The name of the flag (without dashes).
# Dashes will be automatically added (<tt>-</tt> for
# single-character flags, <tt>--</tt> for other flags). This might
@@ -156,15 +207,17 @@
# flag :status
# flag :st => :status
# command :something do
# ...
# end
- def flag(name, &block)
+ def flag(name, description = nil, &block)
if name.is_a? Hash
@parameters << name
else
- @parameters << Flag.new(self, name, &block)
+ flag = Flag.new(self, name, &block)
+ flag.description = description unless description.nil?
+ @parameters << flag
end
end
# Create a new flag with the given name to be used globally
#
@@ -182,11 +235,11 @@
# global_flag :quiet do
# @quiet = true
# end
# @example Define an alias to a global flag
# global_flag :q => :quiet
- def global_flag(name, &block)
+ def global_flag(name, description = nil, &block)
if name.is_a? Hash
name.each do |alias_name, flag_name|
flag = @global_parameters[flag_name]
if flag.nil?
@global_parameters[alias_name] = flag_name
@@ -195,10 +248,11 @@
@global_parameters[alias_name] = flag
end
end
else
flag = Flag.new(self, name, &block)
+ flag.description = description unless description.nil?
@global_parameters.each do |flag_alias, flag_name|
if flag_name == flag.name
@global_parameters[flag_alias] = flag
flag.aliases << flag_alias
end
@@ -215,19 +269,19 @@
# @param (see #option)
# @see #option
# @see Option
# @since 0.2.0
#
- # @example Define a global option
- # global_option :user, 1
- # @example Define a global option with a block to execute
- # global_option :user, 1 do
- # @user = args[0]
+ # @example Define a global option with an optional argument
+ # global_option :user, :login => :optional
+ # @example Define a global option with an argument and a block to execute
+ # global_option :user, :login do
+ # @user = login
# end
# @example Define an alias to a global option
# global_option :u => :user
- def global_option(name, arg_count = 0, &block)
+ def global_option(name, *options, &block)
if name.is_a? Hash
name.each do |alias_name, option_name|
option = @global_parameters[option_name]
if option.nil?
@global_parameters[alias_name] = option_name
@@ -235,11 +289,11 @@
option.aliases << alias_name
@global_parameters[alias_name] = option
end
end
else
- option = Option.new(self, name, arg_count, &block)
+ option = Option.new(self, name, *options, &block)
@global_parameters.each do |option_alias, option_name|
if option_name == option.name
@global_parameters[option_alias] = option
option.aliases << option_alias
end
@@ -250,52 +304,63 @@
# Prompts the user for input
#
# @param [String, #to_s] prompt A String or other Object responding to
# +to_s+ used for displaying a prompt to the user
+ # @param [Array<String>] expected A list of strings that are accepted as
+ # valid input. If not empty, input will be checked and the prompt
+ # will be repeated if required.
# @since 0.2.0
#
# @example Display a prompt "Please type something: "
# command 'interactive' do
# user_provided_value = input 'Please type something'
#
# # Do something with the data
# ...
# end
- def input(prompt = '')
- unless prompt.to_s.empty?
- ostream << "#{prompt}: "
+ #
+ # @example Display a question with validated input
+ # command :question do
+ # good = input 'Do you feel good', 'y', 'n'
+ # ...
+ # end
+ def input(prompt = '', *expected)
+ prompt << " [#{expected.join '/'}]" unless expected.empty?
+ ostream << "#{prompt}: " unless prompt.to_s.empty?
+ input = @settings[:istream].gets[0..-2]
+ unless expected.empty? || expected.include?(input)
+ input = input 'Please provide valid input', *expected
end
- @settings[:istream].gets[0..-2]
+ input
end
# Create a new Option with the given name for the next Command
#
# @param [Symbol, #to_sym] name The name of the Option (without dashes).
# Dashes will be automatically added (+-+ for single-character
# options, +--+ for other options). This might also be a Hash
# where every key will be an alias to the corresponding value,
# e.g. <tt>{ :alias => :option }</tt>.
- # @param [Numeric] arg_count The number of arguments this option takes.
- # Use +0+ for no required arguments or a negative value for an
- # arbitrary number of arguments
+ # @param options (see HasArguments#initialize)
# @param [Proc] block An optional code block that should be executed if
# this option is used
# @see Option
# @since 0.2.0
#
- # @example
- # option :message,1
+ # @example Define an option (and its alias) to a command
+ # option :message, 'A message', :text
# option :m => :message
# command :something do
- # ...
+ # puts message.text
# end
- def option(name, arg_count = 0, &block)
+ def option(name, *options, &block)
if name.is_a? Hash
@parameters << name
else
- @parameters << Option.new(self, name.to_s, arg_count, &block)
+ option = Option.new(self, name.to_s, *options, &block)
+ @parameters << option
end
end
# Convenience method for accessing the user-defined output stream
#
@@ -421,10 +486,11 @@
# class is defined. This is generally useful for simple
# "code and run" applications.
# +colors+:: If +true+, enables colored output using ColoredIO
# +config_file+:: The name of the config file to search
# +config_paths+:: The paths to search for config files
+ # +estream+:: Defines an error output stream to use
# +help_banner+:: Defines a banner for the help message
# +istream+:: Defines an input stream to use
# +name+:: Defines the name of the application
# +ostream+:: Defines an output stream to use
# +raise_errors+:: If +true+, raise errors, otherwise fail gracefully
@@ -432,14 +498,47 @@
# @example
# set :name, 'My App'
# set :autorun, false
def set(setting, value)
setting = setting.to_sym
- unless setting == :ostream
- @settings[setting.to_sym] = value
- else
+ if setting == :estream
+ self.estream = value
+ elsif setting == :ostream
self.ostream = value
+ else
+ @settings[setting.to_sym] = value
end
+ end
+
+ # Saves the current configuration into a configuration file
+ #
+ # The file name and format are specified in the application settings.
+ #
+ # @param [:global, :local, :user, String] Either one of the default
+ # scopes or a specific path. The scopes map to a special path. On
+ # UNIX systems this is +/etc+ for global configurations, the
+ # user's home directory (+~+) for user configurations and the
+ # current working directory (+.+) for local configurations.
+ # @see #default_config=
+ # @since 0.6.0
+ def save_config(scope = :user)
+ case scope
+ when :global
+ if RUBY_PLATFORM.downcase =~ /mswin(?!ce)|mingw|bccwin/
+ path = ENV['ALLUSERSPROFILE']
+ else
+ path = '/etc'
+ end
+ when :local
+ path = File.expand_path '.'
+ when :user
+ path = File.expand_path '~'
+ else
+ path = scope
+ end
+
+ @config_factory.save_config @config,
+ File.join(path, @settings[:config_file])
end
# Displays a throbber while the given block is executed
#
# @param [Proc] block The block to execute while the throbber is