--- title: "Options, Parameters, and Arguments" date: 2019-06-20T15:07:21-04:00 draft: false pre: "4. " weight: 40 --- If you're already an experienced coder, you can jump to the last section of this document, the [Simplified Reference (TLDR)](#simplified-reference-tldr) ## Command Line Structure In the previous section, you saw two parts of the RBCli command line structure - the executable followed by the command. However, RBCli is capable of more complex interaction. The structure is as follows: ``` toolname [options] command [parameters] argument1 argument2... ``` * __Options__ are command line parameters such as `-f`, or `--force`. These are available globally to every command. You can create your own, though several are already built-in and reserved for RBCli: * `--config-file=` allows specifying a config file location manually. * `--generate-config` generates a config file for the user by writing out the defaults to a YAML file. This option will only appear if a config file has been set. The location is configurable, with more on that in the documentation on [User Config Files][user_config_documentation]). * `-v / --version` shows the version. * `-h / --help` shows the help. * __Command__ represents the subcommands that you will create, such as `list`, `test`, or `apply`. * __Parameters__ are the same as options, but only apply to the specific subcommand being executed. In this case only the `-h / --help` parameter is provided automatically. * __Arguments__ are strings that don't begin with a '-', and are passed to the command's code as an array. These can be used as subcommands or additional parameters for your command. So a valid command could look something like these: ```shell mytool -n load --filename=foo.txt mytool parse foo.txt mytool show -l ``` Note that all options and parameters will have both a short and long version of the parameter available for use. So let's take a look at how we define them. ## Options You can find the options declarations under `application/options.rb`. You'll see the example in the code: ```ruby option :name, 'Give me your name', short: 'n', type: :string, default: 'Jack', required: false, permitted: ['Jack', 'Jill'] ``` This won't do for our tool, so let's change it. Remember that these options will be applicable to all of our commands, so lets make it something appropriate: ```ruby option :color, 'Enable color output', short: 'c', type: :boolean, default: false ``` So now, let's take advantage of this flag in our `list` command. Let's change our block to: ```ruby action do |params, args, global_opts, config| Dir.glob "./*" do |filename| outname = filename.split('/')[1] outname += '/' if File.directory? filename # We change the color based on the kind of file shown if global_opts[:color] if File.directory? filename outname = outname.light_blue elsif File.executable? filename outname = outname.light_green end end puts outname end end ``` Notice how we referenced the value by using `global_opts[:color]`. It's that simple. To see it in action, run: ```bash mytool -c list ``` ## Parameters Parameters work the same way as options, but they are localized to only the selected command. They are declared - as you probably guessed by now - in the command's class. So let's add the following lines to our list command within the class declaration: ```ruby parameter :sort, 'Sort output alphabetically', type: :boolean, default: false parameter :all, 'Show hidden files', type: :boolean, default: false parameter :directoriesfirst, 'Show directories on top', type: :boolean, default: false ``` And let's modify our action block to utilize them: ```ruby action do |params, args, global_opts, config| filelist = [] # We include dotfiles if specified include_dotfiles = (params[:all]) ? File::FNM_DOTMATCH : 0 # We store a list of the files in an array, including dotfiles if specified Dir.glob "./*", include_dotfiles do |filename| outname = filename.split('/')[1] outname += '/' if File.directory? filename filelist.append outname end # Sort alphabetically if specified filelist.sort! if params[:sort] # Put directories first if specified if params[:directoriesfirst] files = []; dirs = [] filelist.each do |filename| if File.directory? filename dirs.append(filename) else files.append(filename) end end filelist = dirs + files end # Apply color. We do this at the end now because color codes can alter the sorting. filelist.map! do |filename| if File.directory? filename filename.light_blue elsif File.executable? filename filename.light_green else filename end end if global_opts[:color] puts filelist end ``` You should be able to run it now: ```bash mytool -c list -asd ``` Note how the parameters come after the `list` command in the syntax above. As you create more commands, each will have its own unique set of parameters, while the options remain _before_ the command and are available to all of them. ### User Prompting There is an additional option when declaring parameters to prompt the user for a value if not entered on the command line. This can be done with the `prompt:` keyword. Let's change one of our parameters to utilize it: ```ruby parameter :sort, 'Sort output alphabetically', type: :boolean, default: false, prompt: "Sort output alphabetically?" ``` Now, let's run the tool while omitting the `--sort` parameter, as such: ```bash mytool -c list -ad ``` This should give you the prompt: ``` Sort output alphabetically? (y/N): ``` Because we set the parameter to default to `false` the default here is `N`, which is used if the user hits enter without entering a letter. If the default was set to `true`, then the `Y` would be capitalized and be the default. For more information, see the documentation on [Interactive Commands][interactive_commands]. ## Arguments Lastly on the command line, there are arguments. Arguments are simply strings without the `-` character in front, and automatically get passed into an array in your applicaiton. Let's take a look at how we can use them. Unlike options and parameters, arguments require no setup. So let's assume that we want any arguments passed to the `list` command to be filenames that you want to display, and that you can pass multiple ones. Since arguments aren't listed automatically by the help function, this is a good example of what to put in your usage text. Let's take a look at what our class looks like now: ```ruby class List < Rbcli::Command description %q{List files in current directory} usage <<-EOF To list only specific files, you can enter filenames as arguments mytool list filename1 filename2... EOF parameter :sort, 'Sort output alphabetically', type: :boolean, default: false parameter :all, 'Show hidden files', type: :boolean, default: false parameter :directoriesfirst, 'Show directories on top', type: :boolean, default: false action do |params, args, global_opts, config| filelist = [] # We include dotfiles if specified include_dotfiles = (params[:all]) ? File::FNM_DOTMATCH : 0 # We store a list of the files in an array, including dotfiles if specified Dir.glob "./*", include_dotfiles do |filename| outname = filename.split('/')[1] next unless args.include? outname if args.length > 0 outname += '/' if File.directory? filename filelist.append outname end # Sort alphabetically if specified filelist.sort! if params[:sort] # Put directories first if specified if params[:directoriesfirst] files = []; dirs = [] filelist.each do |filename| if File.directory? filename dirs.append(filename) else files.append(filename) end end filelist = dirs + files end # Apply color. We do this at the end because color codes can alter the sorting filelist.map! do |filename| if File.directory? filename filename.light_blue elsif File.executable? filename filename.light_green else filename end end if global_opts[:color] puts filelist end end ``` ## Simplified Reference (TLDR) RBCli enforces a CLI structure of: ``` toolname [options] command [parameters] argument1 argument2... ``` __Options__ are declared in `application/options.rb` file. __Parameters__ are declared in the respective command's class declaration. __Arguments__ don't need to be declared, and are passed in as an array to your commands. It is helpful to describe the argument purpose in the `usage` text declaration so that the user can see what to do in the help. __Options__ and __parameters__ both use the same format: ```ruby option :, "", short: '', type: , default: , permitted: [, "", short: '', type: , default: , permitted: [}} [regex_explanation]: https://medium.com/factory-mind/regex-tutorial-a-simple-cheatsheet-by-examples-649dc1c3f285 [interactive_commands]: {{< ref "advanced/interactive_commands" >}}