cli-applications in Ruby
Objectives
Understand the Structure of a CLI Application
Run a CLI Application
Understand the CLI User Interface
CLI File Structure
As our applications increase in complexity we'll have to keep our project files well organized. There's a pretty standard convention for where to put code in a project based on what the code does.
We're going to learn a simplified pattern for organizing code in a Ruby application. We'll build on this structure.
A Simple Ruby CLI Application
From the root directory of a Ruby application, you should see a folder structure similar to the following:
You might not have all those folders or those exact files, but you'll have a similar structure.
You'll see top-level directories (the top most folders within your project) such as: bin
, lib
, config
, spec
and sometimes app
.
You also might see some top-level files (files located directly within your project) such as: .learn
, .rspec
, Gemfile
, or Rakefile
. On some labs you might see an actual program file on the top level, like ttt.rb
.
We try to always tell you where the files you need to read or edit are located in a particular lab. This is just in general.
cd
into the directory of a lab you recently solved in terminal. Within that directory type ls -lah
to list all the files in the current directory, including hidden files, in a human order. You should see something similar.
Let's talk about what kind of code goes where.
bin/
bin/
Within the bin/
directory we generally put code that relates to running our actual program. Our executable files that we put in bin are described below, running CLI applications.
config/
config/
A complex program might require 100s of individual files containing source code, method definitions, classes, and more that together constitute all the code required to run the application. We call this code the application's environment. We generally put all the code required to initialize the environment within config/
. We might see that in a config/environment.rb
file or even an entire config/environments/
directory.
What does it mean to "initialize a program's environment?" Establishing the environment for your program can involve a number of things, but on the most basic level, the config file or directory is responsible for things like file requirements (i.e. making sure your different files have access to one another), establishing connections to your database (if you have one) and ensuring that your test suite has access to the files that contain the code it is testing.
lib/
(app/
)
lib/
(app/
)The lib/
or Library directory in most Ruby programs and the app/
directory in Rails projects or complex Ruby programs, is where the majority of our code lives. Within this directory are all the files that define what our program can do. All of the methods and classes our program needs are defined within the files in this directory. One file might define a group of methods that can search for a song by an artist, another file might define a group of methods that can search for a song by a genre. Together these methods might interact to create a Music Search application. We spend the majority of our time building code in this directory.
spec/
(test/
)
spec/
(test/
).rspec
, .learn
, Gemfile
, Gemfile.lock
, Rakefile
.rspec
, .learn
, Gemfile
, Gemfile.lock
, Rakefile
Advanced: A gem is a library of code that you can include in your Ruby program to lend it the capabilities of that library.
Running CLI Applications
In order to run our program from the command line and allow our user to interact with our program as described above, we need to set up a few things.
First, your program needs a bin
directory. "Bin" is short for "binary" and is just another way to refer to executable files. Accordingly, your executable files belong in this directory.
Executable files are any files that contain instructions in a form that a computer's operating system or application can understand and follow. Any executable files we place in our bin directory need to begin with the following line:
#!/usr/bin/env ruby
This is often referred to as a "shebang line" and it tells the shell which interpreter to use to execute the remainder of the file.
Using the above setup, you can run your program by typing ruby bin/< your file name >
into the command line.
Alternatively, you can execute your program by simply typing ./bin/< your file name >
into the command line, since the shebang line at the top of your executable file is already telling the shell to use Ruby to interpret the rest of the file.
Generally our executable file is responsible for running our program. That might include loading required libraries and starting off an execution flow, like telling Ruby to start a game of Tic Tac Toe.
File Permissions and chmod
chmod
For security purposes, a shell environment, including BASH, running within your terminal, requires that executable files are given explicit permission to execute.
When we execute code through the ruby interpreter with the ruby
command, your shell or terminal has already given the ruby
command permission to execute code.
But in order for your shell to execute a file via a command like ./bin/<file name>
, you have to grant it execute permissions. We do this using the chmod
command. You can grant a file execute permissions with:
So to grant a file bin/tictactoe
permissions to execute, you would run: chmod +x bin/tictactoe
. Depending on your shell environment and user, you might need to run chmod
with sudo
(sudo chmod +x bin/tictactoe
).
All the files provided by Learn already have the correct permissions and this should never cause you a problem. But in the event you need to ever create your own executable, we thought we'd tell you.
The CLI Interface
CLI Applications generally follow a similar interface or user experience pattern. Imagine a CLI version of Tic Tac Toe. From a player's perspective, they would start the game by executing the bin
for the game.
The program will execute and generally greet the user with some text output:
The CLI will prompt the user for input and will hang until the user types something and presses enter. The CLI generally gives instructions for the expected input at a given prompt. In the example above, the greeting ends by asking the user if they would like to play.
User Input
The (Y/n)
at the end is a common convention for telling the user that the following prompt is looking for a "Yes" or "No" input as an answer. It is also saying it expects that input as a single character, either y
or n
. The capital Y
is suggesting the default input if the user types nothing and simply presses enter. Generally CLIs will accept "Yes"/"No" in many forms (yes
, no
, N
, n
) and still use the y/n
convention.
↵ is a Carriage Return symbol and simply means the "Enter" key was pressed. It is not literal. In the line above it is used to mean that the user entered in the y
character at an input prompt and then pressed enter.
For more complex interactions, the CLI must inform the user about the custom input prompt, just like in the example above: Please select a square by entering 1-9, 1 for the top left and 9 for the bottom right:
. We just tell the player to enter a number, 1 through 9, to represent the square.
Etc...
That is a simple CLI interface pattern. Whenever you ask the user for input, you will need to:
Prompt the user for input:
Would you like to play? (Y/n)
Define the input interface:
Please select a square by entering 1-9, 1 for the top left and 9 for the bottom right:
Accept user input by yielding to a prompt and waiting patiently for the user to press enter. If the user never enters anything, the program will wait at this state forever until the process is otherwise terminated.
Take the user input and execute the appropriate sub-routine or procedure that represents that feature.
Last updated