Skip to content

module Athena::Console::Input::Interface #

Athena::Console uses a dedicated interface for representing an input source. This allows it to have multiple more specialized implementations as opposed to being tightly coupled to STDIN or a raw IO. This interface represents the methods that must be implemented, however implementations can add additional functionality.

All input sources follow the docopt standard, used by many CLI utility tools. Documentation on this type covers functionality/logic common to all inputs. See each type for more specific information.

Option and argument values can be accessed via ACON::Input::Interface#option and ACON::Input::Interface#argument respectively. There are two overloads, the first accepting just the name of the option/argument as a String, returning the raw value as a String?, with arrays being represented as a comma separated list. The other two overloads accept a T.class representing the desired type the value should be parsed as. For example, given a command with two required and one array arguments:

protected def configure : Nil
  self
    .argument("bool", :required)
    .argument("int", :required)
    .argument("floats", :is_array)
end

Assuming the invocation is ./console test false 10 3.14 172.0 123.7777, the values could then be accessed like:

protected def execute(input : ACON::Input::Interface, output : ACON::Output::Interface) : ACON::Command::Status
  input.argument "bool"       # => "false" : String
  input.argument "bool", Bool # => false : Bool
  input.argument "int", Int8  # => 10 : Int8

  input.argument "floats"                 # => "3.14,172.0,123.7777" : String
  input.argument "floats", Array(Float64) # => [3.14, 172.0, 123.7777] : Array(Float64)

  ACON::Command::Status::SUCCESS
end

The latter syntax is preferred since it correctly types the value. If a provided value cannot be converted to the expected type, an ACON::Exception::Logic exception will be raised. E.g. '123' is not a valid 'Bool'..

Tip

Argument/option modes can be combined. E.g.ACON::Input::Argument::Mode[:required, :is_array] for a required array argument.

There are a lot of possible combinations in regards to what options are defined versus those are provided. To better illustrate how these cases are handled, let's look at an example of a command with three ACON::Input::Options:

protected def configure : Nil
  self
    .option("foo", "f")
    .option("bar", "b", :required)
    .option("baz", "z", :optional)
end

The value of foo will either be true if provided, otherwise false; this is the default behavior of ACON::Input::Options. The bar (b) option is required to have a value. A value can be separated from the option's long name by either a space or = or by its short name by an optional space. Finally, the baz (z) option's value is optional.

This table shows how the value of each option based on the provided input:

Input foo bar baz
--bar=Hello false "Hello" nil
--bar Hello false "Hello" nil
-b=Hello false "=Hello" nil
-b Hello false "Hello" nil
-bHello false "Hello" nil
-fzWorld -b Hello true "Hello" "World"
-zfWorld -b Hello false "Hello" "fWorld"
-zbWorld false nil "bWorld"

Things get a bit trickier when an optional ACON::Input::Argument:

protected def configure : Nil
  self
    .option("foo", "f")
    .option("bar", "b", :required)
    .option("baz", "z", :optional)
    .argument("arg", :optional)
end

In some cases you may need to use the special -- option in order to denote later values should be parsed as arguments, not as a value to an option:

Input bar baz arg
--bar Hello "Hello" nil nil
--bar Hello World "Hello" nil "World"
--bar "Hello World" "Hello World" nil nil
--bar Hello --baz World "Hello" "World" nil
--bar Hello --baz -- World "Hello" nil "World"
-b Hello -z World "Hello" "World" nil

Argument/Option Value Completion#

If the completion script is installed, command and option names will be auto completed by the shell. However, value completion may also be implemented in custom commands by providing the suggested values for a particular option/argument.

@[ACONA::AsCommand("greet")]
class GreetCommand < ACON::Command
  protected def configure : Nil
    # The suggested values do not need to be a static array,
    # they could be sourced via a class/instance method, a constant, etc.
    self
      .argument("name", suggested_values: ["Jim", "Bob", "Sally"])
  end

  # ...
end

Additionally, a block version of ACON::Command#argument(name,mode,description,default,&) and ACON::Command#option(name,shortcut,value_mode,description,default,&) may be used if more complex logic is required.

@[ACONA::AsCommand("greet")]
class GreetCommand < ACON::Command
  protected def configure : Nil
    self
      .argument("name") do |input|
        # The value the user already typed, e.g. the value the user already typed,
        # e.g. when typing "greet Ge" before pressing Tab, this will contain "Ge".
        current_value = input.completion_value

        # Get the list of username names from somewhere (e.g. the database)
        # you may use current_value to filter down the names
        available_usernames = ...

        # then suggested the usernames as values
        return available_usernames
      end
  end

  # ...
end

Tip

The shell completion script is able to handle huge amounts of suggestions and will automatically filter the values based on existing input from the user. You do not have to implement any filter logic in the command. input.completion_value can still be used to filter if it helps with performance, such as reducing amount of rows the DB returns.

Direct including types

Athena::Console::Input::Streamable

Methods#

abstract #argument(name : String, type : T.class) forall T#

Returns the value of the argument with the provided name converted to the desired type. This method is preferred over #argument since it provides better typing.

Raises an ACON::Exception::Logic if the actual argument value could not be converted to a type.

abstract #argument(name : String) : String | Nil#

Returns the raw string value of the argument with the provided name, or nil if is optional and was not provided.

abstract #arguments : ::Hash#

Returns a ::Hash representing the keys and values of the parsed arguments of self.

abstract #bind(definition : ACON::Input::Definition) : Nil#

Binds the provided definition to self. Essentially provides what should be parsed from self.

abstract #first_argument : String | ::Nil#

Returns the first argument from the raw un-parsed input. Mainly used to get the command that should be executed.

abstract #has_argument?(name : String) : Bool#

Returns true if self has an argument with the provided name, otherwise false.

abstract #has_option?(name : String) : Bool#

Returns true if self has an option with the provided name, otherwise false.

abstract #has_parameter?(*values : String, only_params : Bool = false) : Bool#

Returns true if the raw un-parsed input contains one of the provided values.

This method is to be used to introspect the input parameters before they have been validated. It must be used carefully. It does not necessarily return the correct result for short options when multiple flags are combined in the same option.

If only_params is true, only real parameters are checked. I.e. skipping those that come after the -- option.

abstract #interactive=(interactive : Bool)#

Sets if self is #interactive?.

abstract #interactive? : Bool#

Returns true if self represents an interactive input, such as a TTY.

abstract #option(name : String, type : T.class) forall T#

Returns the value of the option with the provided name converted to the desired type. This method is preferred over #option since it provides better typing.

Raises an ACON::Exception::Logic if the actual option value could not be converted to a type.

abstract #option(name : String) : String | Nil#

Returns the raw string value of the option with the provided name, or nil if is optional and was not provided.

abstract #options : ::Hash#

Returns a ::Hash representing the keys and values of the parsed options of self.

abstract #parameter(value : String, default : _ = false, only_params : Bool = false)#

Returns the value of a raw un-parsed parameter for the provided value..

This method is to be used to introspect the input parameters before they have been validated. It must be used carefully. It does not necessarily return the correct result for short options when multiple flags are combined in the same option.

If only_params is true, only real parameters are checked. I.e. skipping those that come after the -- option.

abstract #set_argument(name : String, value : _) : Nil#

Sets the value of the argument with the provided name.

abstract #set_option(name : String, value : _) : Nil#

Sets the value of the option with the provided name.

abstract #to_s(io : IO) : Nil#

Returns a string representation of the args passed to the command.

abstract #validate : Nil#

Validates the input, asserting all of the required parameters are provided. Raises ACON::Exception::Runtime when not enough arguments are given.