postmodern/command_mapper.rb

View on GitHub
lib/command_mapper/argument.rb

Summary

Maintainability
A
1 hr
Test Coverage
require_relative 'exceptions'
require_relative 'arg'

module CommandMapper
  #
  # Represents an additional argument of a command.
  #
  class Argument < Arg

    # The argument name.
    #
    # @return [Symbol]
    attr_reader :name

    #
    # Initializes the argument.
    #
    # @param [Symbol] name
    #   The argument's name.
    #
    # @param [Boolean] required
    #   Specifies whether the argument is required or can be omitted.
    #
    # @param [Types::Type, Hash] type
    #   The value type of the argument.
    #
    # @param [Boolean] repeats
    #   Specifies whether the argument can be given multiple times.
    #
    # @raise [ArgumentError]
    #   The given `type:` must not be `false` or `nil`.
    #
    # @api private
    #
    def initialize(name, required: true, type: Types::Str.new, repeats: false)
      super(required: required, type: type)

      @name    = name
      @repeats = repeats
    end

    #
    # Indicates whether the arg can be repeated multiple times or not.
    #
    # @return [Boolean]
    #
    def repeats?
      @repeats
    end

    #
    # Validates whether a given value is compatible with the arg.
    #
    # @param [Array<Object>, Object] value
    #   The given value to validate.
    #
    # @return [true, (false, String)]
    #   Returns true if the value is valid, or `false` and a validation error
    #   message if the value is not compatible.
    #
    # @api semipublic
    #
    def validate(value)
      if repeats?
        values = case value
                 when Array then value
                 else            [value]
                 end

        if required?
          # argument requires atleast one value
          if values.empty?
            return [false, "requires at least one value"]
          end
        end

        # validate each element in the value
        values.each do |element|
          valid, message = @type.validate(element)

          unless valid
            return [valid, message]
          end
        end

        return true
      else
        super(value)
      end
    end

    #
    # Converts the given value into the command-line arguments for the
    # argument's flag and value.
    #
    # @param [Array] argv
    #   The argv array.
    #
    # @param [Object] value
    #   The value for the argument.
    #
    # @return [Array<String>]
    #   The command-line arguments.
    #
    # @raise [ArgumentError]
    #   The given value was incompatible with the argument.
    #
    # @api semipublic
    #
    def argv(argv=[],value)
      valid, message = validate(value)

      unless valid
        raise(ValidationError,"argument #{@name} was given an invalid value (#{value.inspect}): #{message}")
      end

      if repeats?
        values = Array(value)

        values.each do |element|
          emit_arg_value(argv,element)
        end
      else
        emit_arg_value(argv,value)
      end

      return argv
    end

    private

    #
    # Emits a single command-line arg value.
    #
    # @param [Array<String>] argv
    #   The argv array to append to.
    #
    # @param [Object] value
    #   The value for the argument.
    #
    def emit_arg_value(argv,value)
      # explicitly ignore nil values
      unless value.nil?
        argv << @type.format(value)
      end
    end

  end
end