lib/command_mapper/command.rb in command_mapper-0.1.2 vs lib/command_mapper/command.rb in command_mapper-0.2.0
- old
+ new
@@ -107,30 +107,56 @@
command = new(params,**kwargs,&block)
command.run_command
end
#
- # Runs the command in a shell and captures all stdout output.
+ # Initializes and spawns the command as a separate process, returning the
+ # PID of the process.
#
# @param [Hash{Symbol => Object}] params
# The option values.
#
# @yield [self]
# The newly initialized command.
#
# @yieldparam [Command] self
#
+ # @return [Integer]
+ # The PID of the new command process.
+ #
+ # @raise [Errno::ENOENT]
+ # The command could not be found.
+ #
+ # @since 0.2.0
+ #
+ def self.spawn(params={},**kwargs,&block)
+ command = new(params,**kwargs,&block)
+ command.spawn_command
+ end
+
+ #
+ # Initializes and runs the command in a shell and captures all stdout
+ # output.
+ #
+ # @param [Hash{Symbol => Object}] params
+ # The option values.
+ #
+ # @yield [self]
+ # The newly initialized command.
+ #
+ # @yieldparam [Command] self
+ #
# @return [String]
# The stdout output of the command.
#
def self.capture(params={},**kwargs,&block)
command = new(params,**kwargs,&block)
command.capture_command
end
#
- # Executes the command and returns an IO object to it.
+ # Initializes and executes the command and returns an IO object to it.
#
# @param [Hash{Symbol => Object}] params
# The option values.
#
# @yield [self]
@@ -144,11 +170,11 @@
command = new(params,**kwargs,&block)
command.popen_command
end
#
- # Initializes and runs the command through sudo.
+ # Initializes and runs the command through `sudo`.
#
# @param [Hash{Symbol => Object}] params
# The option values.
#
# @param [Hash{Symbol => Object}] kwargs
@@ -224,22 +250,35 @@
{}
end
end
#
+ # Determines if an option with the given name has been defined.
+ #
+ # @param [Symbol] name
+ # The given name.
+ #
+ # @return [Boolean]
+ # Specifies whether an option with the given name has been defined.
+ #
+ # @api semipublic
+ #
+ # @since 0.2.0
+ #
+ def self.has_option?(name)
+ options.has_key?(name)
+ end
+
+ #
# Defines an option for the command.
#
# @param [String] flag
# The option's command-line flag.
#
# @param [Symbol, nil] name
# The option's name.
#
- # @param [Boolean] equals
- # Specifies whether the option's flag and value should be separated with a
- # `=` character.
- #
# @param [Hash, nil] value
# The option's value.
#
# @option value [Boolean] :required
# Specifies whether the option requires a value or not.
@@ -248,10 +287,18 @@
# The explicit type for the option's value.
#
# @param [Boolean] repeats
# Specifies whether the option can be given multiple times.
#
+ # @param [Boolean] equals
+ # Specifies whether the option's flag and value should be separated with a
+ # `=` character.
+ #
+ # @param [Boolean] value_in_flag
+ # Specifies that the value should be appended to the option's flag
+ # (ex: `-Fvalue`).
+ #
# @api public
#
# @example Defining an option:
# option '--foo'
#
@@ -262,39 +309,52 @@
# option '--file', value: true
#
# @example Defining an option who's value is optional:
# option '--file', value: {required: false}
#
+ # @example Defining an `-Fvalue` option:
+ # option '--foo', value: true, value_in_flag: true
+ #
# @example Defining an `--opt=value` option:
# option '--foo', equals: true, value: true
#
# @example Defining an option that can be repeated multiple times:
# option '--foo', repeats: true
#
# @example Defining an option that takes a comma-separated list:
# option '--list', value: List.new
#
# @raise [ArgumentError]
- # The option flag conflicts with a pre-existing internal method.
+ # The option flag conflicts with a pre-existing internal method, or
+ # another argument or subcommand.
#
- def self.option(flag, name: nil, equals: nil, value: nil, repeats: false, &block)
+ def self.option(flag, name: nil, value: nil, repeats: false,
+ # formatting options
+ equals: nil,
+ value_in_flag: nil,
+ &block)
option = Option.new(flag, name: name,
- equals: equals,
value: value,
repeats: repeats,
+ # formatting options
+ equals: equals,
+ value_in_flag: value_in_flag,
&block)
- self.options[option.name] = option
-
if is_internal_method?(option.name)
if name
raise(ArgumentError,"option #{flag.inspect} with name #{name.inspect} cannot override the internal method with same name: ##{option.name}")
else
raise(ArgumentError,"option #{flag.inspect} maps to method name ##{option.name} and cannot override the internal method with same name: ##{option.name}")
end
+ elsif has_argument?(option.name)
+ raise(ArgumentError,"option #{flag.inspect} with name #{option.name.inspect} conflicts with another argument with the same name")
+ elsif has_subcommand?(option.name)
+ raise(ArgumentError,"option #{flag.inspect} with name #{option.name.inspect} conflicts with another subcommand with the same name")
end
+ self.options[option.name] = option
attr_accessor option.name
end
#
# All defined options.
@@ -310,10 +370,27 @@
{}
end
end
#
+ # Determines if an argument with the given name has been defined.
+ #
+ # @param [Symbol] name
+ # The given name.
+ #
+ # @return [Boolean]
+ # Specifies whether an argument with the given name has been defined.
+ #
+ # @api semipublic
+ #
+ # @since 0.2.0
+ #
+ def self.has_argument?(name)
+ arguments.has_key?(name)
+ end
+
+ #
# Defines an option for the command.
#
# @param [Symbol] name
#
# @param [Boolean] required
@@ -335,24 +412,28 @@
#
# @example Define an optional argument:
# argument :file, required: false
#
# @raise [ArgumentError]
- # The argument name conflicts with a pre-existing internal method.
+ # The argument name conflicts with a pre-existing internal method, or
+ # another option or subcommand.
#
def self.argument(name, required: true, type: Str.new, repeats: false)
name = name.to_sym
argument = Argument.new(name, required: required,
type: type,
repeats: repeats)
- self.arguments[argument.name] = argument
-
if is_internal_method?(argument.name)
raise(ArgumentError,"argument #{name.inspect} cannot override internal method with same name: ##{argument.name}")
+ elsif has_option?(argument.name)
+ raise(ArgumentError,"argument #{name.inspect} conflicts with another option with the same name")
+ elsif has_subcommand?(argument.name)
+ raise(ArgumentError,"argument #{name.inspect} conflicts with another subcommand with the same name")
end
+ self.arguments[argument.name] = argument
attr_accessor name
end
#
# All defined subcommands.
@@ -368,10 +449,27 @@
{}
end
end
#
+ # Determines if a subcommand with the given name has been defined.
+ #
+ # @param [Symbol] name
+ # The given name.
+ #
+ # @return [Boolean]
+ # Specifies whether a subcommand with the given name has been defined.
+ #
+ # @api semipublic
+ #
+ # @since 0.2.0
+ #
+ def self.has_subcommand?(name)
+ subcommands.has_key?(name)
+ end
+
+ #
# Defines a subcommand.
#
# @param [String] name
# The name of the subcommand.
#
@@ -394,29 +492,34 @@
# end
# end
# end
#
# @raise [ArgumentError]
- # The subcommand name conflicts with a pre-existing internal method.
+ # The subcommand name conflicts with a pre-existing internal method, or
+ # another option or argument.
#
def self.subcommand(name,&block)
- name = name.to_s
+ name = name.to_s
+ method_name = name.tr('-','_')
+ class_name = name.split(/[_-]+/).map(&:capitalize).join
+ subcommand_name = method_name.to_sym
+ if is_internal_method?(method_name)
+ raise(ArgumentError,"subcommand #{name.inspect} maps to method name ##{method_name} and cannot override the internal method with same name: ##{method_name}")
+ elsif has_option?(subcommand_name)
+ raise(ArgumentError,"subcommand #{name.inspect} conflicts with another option with the same name")
+ elsif has_argument?(subcommand_name)
+ raise(ArgumentError,"subcommand #{name.inspect} conflicts with another argument with the same name")
+ end
+
subcommand_class = Class.new(Command)
subcommand_class.command(name)
subcommand_class.class_eval(&block)
- method_name = name.tr('-','_')
- class_name = name.split(/[_-]+/).map(&:capitalize).join
-
- self.subcommands[method_name.to_sym] = subcommand_class
+ self.subcommands[subcommand_name] = subcommand_class
const_set(class_name,subcommand_class)
- if is_internal_method?(method_name)
- raise(ArgumentError,"subcommand #{name.inspect} maps to method name ##{method_name} and cannot override the internal method with same name: ##{method_name}")
- end
-
define_method(method_name) do |&block|
if block then @command_subcommand = subcommand_class.new(&block)
else @command_subcommand
end
end
@@ -531,19 +634,35 @@
return escaped_command
end
#
- # Initializes and runs the command.
+ # Runs the command.
#
# @return [Boolean, nil]
#
def run_command
- system(@command_env,*command_argv)
+ Kernel.system(@command_env,*command_argv)
end
#
+ # Spawns the command as a separate process, returning the PID of the
+ # process.
+ #
+ # @return [Integer]
+ # The PID of the new command process.
+ #
+ # @raise [Errno::ENOENT]
+ # The command could not be found.
+ #
+ # @since 0.2.0
+ #
+ def spawn_command
+ Process.spawn(@command_env,*command_argv)
+ end
+
+ #
# Runs the command in a shell and captures all stdout output.
#
# @return [String]
# The stdout output of the command.
#
@@ -561,10 +680,10 @@
else IO.popen(@command_env,command_argv)
end
end
#
- # Initializes and runs the command through sudo.
+ # Runs the command through `sudo`.
#
# @param [Hash{Symbol => Object}] sudo_params
# Additional keyword arguments for {Sudo#initialize}.
#
# @return [Boolean, nil]