├── .DS_Store ├── .gitignore ├── README.md ├── awesome_cli ├── lib ├── awesome_cli.ex └── awesome_cli │ └── supervisor.ex ├── mix.exs └── test ├── awesome_cli_test.exs └── test_helper.exs /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rizafahmi/elixirdose-cli/917231dcf753d9bad664e91218f7700d92ef6037/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /_build 2 | /deps 3 | erl_crash.dump 4 | *.ez 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Create Command Line Tools 2 | 3 | As software developers, we tend to depend on command line, especially me. Command line interface (CLI) are on fire this current time. Python, Ruby, Erlang and Elixir provide us with awesome tools on command line. 4 | 5 | So in this article we will attempt to creating a command line tools. And I have feeling that Elixir will do great in this area. 6 | 7 | ## Setting Up The Application 8 | 9 | Let’s start with a new project using mix. 10 | 11 | $> mix new awesome_cli 12 | $> cd awesome_cli 13 | 14 | Open up `lib/awesome_cli.ex` and you’ll see something like this: 15 | 16 | defmodule AwesomeCli do 17 | use Application.Behaviour 18 | 19 | # See http://elixir-lang.org/docs/stable/elixir/Application.html 20 | # for more information on OTP Applications 21 | def start(_type, _args) do 22 | AwesomeCli.Supervisor.start_link 23 | end 24 | end 25 | 26 | Let's do me a favor to create hello world message in the project, will you?! 27 | 28 | defmodule AwesomeCli do 29 | use Application.Behaviour 30 | 31 | # See http://elixir-lang.org/docs/stable/elixir/Application.html 32 | # for more information on OTP Applications 33 | def start(_type, _args) do 34 | AwesomeCli.Supervisor.start_link 35 | end 36 | 37 | def main(args) do 38 | IO.puts "Hello, world!" 39 | end 40 | end 41 | 42 | Now run `mix escript.build` to make it executeable. 43 | 44 | $> mix escript.build 45 | 46 | If you get error like: `** (Mix) Could not generate escript, please set :escript in your project configuration to a function that returns the escript configuration for our application. So let's do that by opening `mix.exs` file. 47 | 48 | defmodule AwesomeCli.Mixfile do 49 | use Mix.Project 50 | 51 | def project do 52 | [app: :awesome_cli, 53 | version: "0.0.1", 54 | elixir: "~> 1.0.4", 55 | escript: escript, 56 | deps: deps] 57 | end 58 | 59 | def escript do 60 | [main_module: AwesomeCli] 61 | end 62 | 63 | 64 | def application do 65 | [ applications: [], 66 | mod: { AwesomeCli, [] } ] 67 | end 68 | 69 | defp deps do 70 | [] 71 | end 72 | end 73 | 74 | Let's rerun `mix escript.build` and mix will compile our awesome_cli.ex file and 75 | generate a file `Elixir.AwesomeCli.beam` in the `_build/dev/lib/awesome_cli/ebin` 76 | directory as well as one executable file called `awesome_cli`. Let's execute the file. 77 | 78 | $> ./awesome_cli 79 | Hello, world! 80 | 81 | There you have it our first Elixir-powered executable application! 82 | 83 | ## Parsing Argument(s) 84 | 85 | Lucky us, Elixir has [OptionParser](http://elixir-lang.org/docs/stable/elixir/OptionParser.html) 86 | for parsing CLI argument(s). We will use this module to create an awesome command line tools that 87 | will get an argument or two from user. 88 | 89 | First thing first, we will create command line tools that will say hello to name we given. 90 | We will do something like: `./awesome_cli --name ElixirFriend`. 91 | 92 | Open up `lib/awesome_cli.ex` and add code below: 93 | 94 | def main(args) do 95 | args |> parse_args 96 | end 97 | 98 | def parse_args(args) do 99 | {[name: name], _, _} = OptionParser.parse(args) 100 | 101 | IO.puts "Hello, #{name}! You're awesome!!" 102 | end 103 | 104 | We used `|>` operator to passing an argument to `parse_args` function. Then we used 105 | `OptionParser.parse` to parse the argument, take exactly one argument then print it out. 106 | Whe we run `mix escript.build` again then execute the app, we got something like this. 107 | 108 | $> mix escript.build 109 | $> ./awesome_cli --name ElixirFriend 110 | Hello, ElixirFriend! You're awesome!! 111 | 112 | Awesome, right?! Ok, now to make our cli more awesome, let's implement help message to 113 | guide user how to use this tool. Let's use `case` syntax to and pattern matching for this case. 114 | 115 | def parse_args(args) do 116 | options = OptionParser.parse(args) 117 | 118 | case options do 119 | {[name: name], _, _} -> IO.puts "Hello, #{name}! You're awesome!!" 120 | {[help: true], _, _} -> IO.puts "This is help message" 121 | 122 | end 123 | end 124 | 125 | Rerun `mix escript.build` again and execute it with `--help` option. 126 | 127 | $> ./awesome_cli --name ElixirFriend 128 | Hello, ElixirFriend! You're awesome!! 129 | $> ./awesome_cli --help 130 | This is help message 131 | 132 | ## Finishing Touch 133 | 134 | Let's refactor the code for little bit. First, we will make `parse_args` just for 135 | parsing arguments and return something to be used in another function. 136 | 137 | def main(args) do 138 | args |> parse_args |> do_process 139 | end 140 | 141 | def parse_args(args) do 142 | options = OptionParser.parse(args) 143 | 144 | case options do 145 | {[name: name], _, _} -> [name] 146 | {[help: true], _, _} -> :help 147 | _ -> :help 148 | 149 | end 150 | end 151 | 152 | def do_process([name]) do 153 | IO.puts "Hello, #{name}! You're awesome!!" 154 | end 155 | 156 | def do_process(:help) do 157 | IO.puts """ 158 | Usage: 159 | ./awesome_cli --name [your name] 160 | 161 | Options: 162 | --help Show this help message. 163 | 164 | Description: 165 | Prints out an awesome message. 166 | """ 167 | 168 | System.halt(0) 169 | end 170 | 171 | For the last time, rerun `mix escript.build` then try to execute it. 172 | 173 | $> mix escript.build 174 | $> ./awesome_cli --name ElixirFriend 175 | Hello, ElixirFriend! You're awesome!! 176 | $> ./awesome_cli --help 177 | Usage: 178 | ./awesome_cli --name [your name] 179 | 180 | Options: 181 | --help Show this help message. 182 | 183 | Description: 184 | Prints out an awesome message. 185 | $> ./awesome_cli 186 | Usage: 187 | ./awesome_cli --name [your name] 188 | 189 | Options: 190 | --help Show this help message. 191 | 192 | Description: 193 | Prints out an awesome message. 194 | 195 | ## Conclusion 196 | 197 | Today we are using Elixir's `OptionParser` for creating a simple command line tools. 198 | And with help from `mix escript.build` we able to generate the tools became executable. 199 | This example maybe simple enough but with this we can take conclusion that very easy and feel natural to create command line tools with Elixir. 200 | 201 | 202 | ## References 203 | 204 | * [OptionParser Docs](http://elixir-lang.org/docs/stable/elixir/OptionParser.html) 205 | -------------------------------------------------------------------------------- /awesome_cli: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rizafahmi/elixirdose-cli/917231dcf753d9bad664e91218f7700d92ef6037/awesome_cli -------------------------------------------------------------------------------- /lib/awesome_cli.ex: -------------------------------------------------------------------------------- 1 | defmodule AwesomeCli do 2 | use Application 3 | 4 | # See http://elixir-lang.org/docs/stable/elixir/Application.html 5 | # for more information on OTP Applications 6 | def start(_type, _args) do 7 | AwesomeCli.Supervisor.start_link 8 | end 9 | 10 | def main(args) do 11 | args |> parse_args |> do_process 12 | end 13 | 14 | def parse_args(args) do 15 | options = OptionParser.parse(args) 16 | 17 | case options do 18 | {[name: name], _, _} -> [name] 19 | {[help: true], _, _} -> :help 20 | _ -> :help 21 | 22 | end 23 | end 24 | 25 | def do_process([name]) do 26 | IO.puts "Hello, #{name}! You're awesome!!" 27 | end 28 | 29 | def do_process(:help) do 30 | IO.puts """ 31 | Usage: 32 | ./awesome_cli --name [your name] 33 | 34 | Options: 35 | --help Show this help message. 36 | 37 | Description: 38 | Prints out an awesome message. 39 | """ 40 | 41 | System.halt(0) 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /lib/awesome_cli/supervisor.ex: -------------------------------------------------------------------------------- 1 | defmodule AwesomeCli.Supervisor do 2 | use Supervisor 3 | 4 | def start_link do 5 | :supervisor.start_link(__MODULE__, []) 6 | end 7 | 8 | def init([]) do 9 | children = [ 10 | # Define workers and child supervisors to be supervised 11 | # worker(AwesomeCli.Worker, [arg1, arg2, arg3]) 12 | ] 13 | 14 | # See http://elixir-lang.org/docs/stable/elixir/Supervisor.html 15 | # for other strategies and supported options 16 | supervise(children, strategy: :one_for_one) 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /mix.exs: -------------------------------------------------------------------------------- 1 | defmodule AwesomeCli.Mixfile do 2 | use Mix.Project 3 | 4 | def project do 5 | [app: :awesome_cli, 6 | version: "0.0.1", 7 | elixir: "~> 1.0.4", 8 | escript: escript, 9 | deps: deps] 10 | end 11 | 12 | # Configuration for the OTP application 13 | # 14 | # Type `mix help compile.app` for more information 15 | def application do 16 | [ applications: [], 17 | mod: { AwesomeCli, [] } ] 18 | end 19 | 20 | # List all dependencies in the format: 21 | # 22 | # { :foobar, git: "https://github.com/elixir-lang/foobar.git", tag: "0.1" } 23 | # 24 | # Type `mix help deps` for more examples and options 25 | defp deps do 26 | [] 27 | end 28 | 29 | # Configuration for the escript build process 30 | # 31 | # Type `mix help escript.build` for more information 32 | defp escript do 33 | [ main_module: AwesomeCli, 34 | embedd_elixir: true ] 35 | end 36 | 37 | end 38 | -------------------------------------------------------------------------------- /test/awesome_cli_test.exs: -------------------------------------------------------------------------------- 1 | defmodule AwesomeCliTest do 2 | use ExUnit.Case 3 | 4 | test "the truth" do 5 | assert 1 + 1 == 2 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /test/test_helper.exs: -------------------------------------------------------------------------------- 1 | ExUnit.start 2 | --------------------------------------------------------------------------------