A Crystal Story: oh! so many options!
Tags: crystal, cli, tutorial, webdev
Published: 2020-01-21 17:00:01


## Previously on ...

In case you missed it, we are building a [Simple Static File Server](https://dev.to/franciscello/a-crystal-story-simple-static-file-server-5fep) using the [Crystal](https://crystal-lang.org/) language. And in this 2nd episode of the trilogy we are going to **add options** to our File Server. 

Adding options is like making a cake ... mmm, ok ... wait, it has nothing to do with that 😅. Maybe it's more like buying a cake: most of the time we are presented with options 🤔. I know: it's a really bad analogy 🙈 but let's continue ...


## What to parameterize?

When creating our server we need to set the `path` with the files we want to serve and the `port` where our server will listen. Let's go and set these two attributes as command line options to our server.

## Command line options in Crystal

Adding the ability to pass options via command line is really easy and intuitive. 

_wait wait! I got it: adding options is like adding sprinkles to a cake_ 😏

We may read all about it in the section entitled [Command Line Interface Application](https://crystal-lang.org/reference/overview/cli.html) in Crystal's [language reference](https://crystal-lang.org/reference/) (it's worth mentioning that there are examples based on the The Beatles! 😆)

Here's the first example shown in the previous reference:

```crystal
# file: help.cr
require "option_parser"

OptionParser.parse do |parser|
  parser.banner = "Welcome to The Beatles App!"

  parser.on "-v", "--version", "Show version" do
    puts "version 1.0"
    exit
  end
  parser.on "-h", "--help", "Show help" do
    puts parser
    exit
  end
end
```

So, it looks like we need to use the method `parse` defined in `OptionParser`. Inside the block passed to `OptionParser#parse` we will define the options we want our application to have (for example, `-v` or `-h`). Easy!

## Adding options to our Static File Server

As we mention before, we need to define three options:

- `-f`: to set the **files** path
- `-p`: to set the server **port**
- `-h`: show help

The code will look like this:

```crystal
OptionParser.parse do |parser|
  parser.banner = "A Simple Static File Server!"

  parser.on "-f PATH", "--files=PATH", "Files path (default: #{path})" do |files_path|
    path = files_path
  end
  parser.on "-p PORT", "--port=PORT", "Port to listen (default: #{port})" do |server_port|
    port = server_port.to_i
  end
  parser.on "-h", "--help", "Show help" do
    puts parser
    exit
  end
end
```

Let's not forget the default values!

```crystal
path = "./public"
port = 8081
```

And that's it! Let's put all together and ...

## ... here is the cake 🍰

_After all, maybe it **is** like making a cake_ 😉

```crystal
# file_server_option.cr
require "http"
require "option_parser"

path = "./public"
port = 8081

OptionParser.parse do |parser|
  parser.banner = "A Simple Static File Server!"

  parser.on "-f PATH", "--files=PATH", "Files path (default: #{path})" do |files_path|
    path = files_path
  end
  parser.on "-p PORT", "--port=PORT", "Port to listen (default: #{port})" do |server_port|
    port = server_port.to_i
  end
  parser.on "-h", "--help", "Show help" do
    puts parser
    exit
  end
end

server = HTTP::Server.new([
  HTTP::LogHandler.new,
  HTTP::ErrorHandler.new,
  HTTP::StaticFileHandler.new(path),
])

address = server.bind_tcp port
puts "Listening on http://#{address} and serving files in path #{path}"
server.listen
```

### Is it working? 🤔

First, let's try showing the help:

```terminal-session
$ crystal file_server_option.cr -- -h
A Simple Static Cake Server!
    -f PATH, --files=PATH            Files path (default: ./public)
    -p PORT, --port=PORT             Port to listen (default: 8081)
    -h, --help                       Show help
```

Now, let's start our server with different `path` and `port`:

```terminal-session
$ crystal file_server_option.cr -- -f"." -p8080
```

>**Note:** we use `--` to pass parameters to our application because we are using the `crystal` command line to run the application. If we use `-f"." -p8080` directly then we would be passing the parameter to `crystal` instead 🙃

The application replies:

```terminal-session
Listening on http://127.0.0.1:8080 and serving files in path .
```

Now, if we navigate to `http://localhost:8080/` then the content of the current folder will be displayed in the browser window.


All done! Piece of cake!🧁


## Farewell and see you later.

Coming next: We are going to dockerize our ~~cake~~ static file server! 🐳

Thanks, thanks, thanks to:
@bcardiff, @diegoliberman and [@petti](https://www.linkedin.com/in/mpettinati/) for reviewing this post and improving the code and text!! 👏🤓

Photo by Hanh Nguyen on Unsplash