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]