Journal CLI
Mix.install([
{:kino, github: "livebook-dev/kino", override: true},
{:kino_lab, "~> 0.1.0-dev", github: "jonatanklosko/kino_lab"},
{:vega_lite, "~> 0.1.4"},
{:kino_vega_lite, "~> 0.1.1"},
{:benchee, "~> 0.1"},
{:ecto, "~> 3.7"},
{:math, "~> 0.7.0"},
{:faker, "~> 0.17.0"},
{:utils, path: "#{__DIR__}/../utils"},
{:tested_cell, git: "https://github.com/BrooklinJazz/tested_cell"}
])
Navigation
Setup
Ensure you type the ea
keyboard shortcut to evaluate all Elixir cells before starting. Alternatively you can evaluate the Elixir cells as you read.
Journal CLI
You’re going to create a Command Line Interface Application, generally referred to as a CLI. This CLI will automate the process of creating daily journals with a markdown template.
Once complete, you will run the following in the command line:
./journal
Which will generate a markdown file based on the current date yyyy-mm-dd.md
with a journal template.
Create A New Mix Project
Using the command line, create a new project in the projects
folder called journal
.
mix new journal
Configure the Project For the Command Line
We want to be able to use the command line to execute this project.
Command line applications run the main/1
function on a configured module.
First, add a main/1
function to the Journal
module in journal.ex
. We’re going to
leave it with a blank IO.inspect/2
for now.
def main(args) do
IO.inspect(args)
end
For a command line application, we need to tell Mix which module to run.
Configure the :escript
option with the :main_module
. Tell Mix to use the Journal
module in mix.exs
.
def project do
[
app: :journal,
version: "0.1.0",
elixir: "~> 1.13",
start_permanent: Mix.env() == :prod,
deps: deps(),
# Added this line
escript: [main_module: Journal]
]
end
Under the hood, Elixir builds an executable file called an escript. This escript can be invoked from the command line directly rather than using the IEx Shell.
Run the following in the command line from the project folder to build the executable file. To pick up future changes, you will need to re-run this command.
mix escript.build
Execute the file with:
./journal
You should see it print an empty list. That’s because we haven’t passed in any arguments.
[]
Pass in arguments to see them in the list.
./journal argument1 argument2 --switch
["argument1", "argument2", "--switch"]
Create A Daily Journal
With no arguments provided, the ./journal
command should create a markdown file yyyy-mm-dd.md
in an entries/
folder.
The files contents should be blank except for a title matching todays date.
# yyyy-mm-dd
Implement the Journal.main/1
function when given no arguments.
Once complete, run the following from the command line.
./journal
And ensure it generated a entries/yyyy-mm-dd.md
file with the following content.
# yyyy-mm-dd
Where yyyy-mm-dd
is the current date. i.e. 2022-05-25
.
Add the –folder Switch
Command line applications use switches to alter functionality. You’ve already used a switch whenever you make a git commit.
git commit -m "my example commit"
-m
is just an alias for message
. So you could also use:
git commit --message "my example commit".
So --message
is a switch for the git
cli.
You’re going to create a --folder
switch for our journal
cli which will override the default entries
folder.
For example, the following will create a new file in the example_folder
.
./journal --folder example_folder
You can use the built-in OptionParser module to parse switches.
Specify the :switches
in a keyword list with their values. For example, a --folder
switch could be handled with:
# journal.ex
def main(args) do
options = [switches: [folder: :string]]
{opts, _, _} = OptionParser.parse(args, options)
IO.inspect(opts)
end
Now opts
for the following command would be: [folder: "example"]
.
./journal --folder example_folder
Implement the --folder
switch to store journal entries in a different folder.
Continue to use test_entries
if the --folder
switch is not provided.
Add the –file Switch
Implement the --file
switch, which should override the default file name.
So the following would create a new file test.md
.
./journal --file test
Add the –title Switch
Implement the --title
switch, which should override the default file title.
So the following would create a new file test.md
.
./journal --file test --title "My Title"
With the following content:
# My Title
Use the –template Switch
Test and implement the --template
switch.
The template flag should allow users to use any custom template in a templates
folder like so:
./journal --template bullet_journal
Where templates/bullet_journal.md
is a markdown file with the following content:
# Bullet Journal
I am grateful for
-
-
-
Today I will
-
-
-
(BONUS) Handle An Existing Daily Journal
Warn users if they already have a daily journal or a journal with the same name.
You can use IO.puts/2
to print a message to the user.
$ ./journal
Error, daily journal already exists. Open test_entries/yyyy-mm-dd.md to see todays journal.
(BONUS) Provide Custom Values in Templates
If a template defines some content in the format {DATE}
,
replace that content with todays date.
If a template defines some content in the format {TITLE}
replace that value
with the --title
switch if it exists. If it does not exist, replace the {TITLE}
with an empty string.
For example:
# {TITLE}
Created on {DATE}
When provided --title "My Title"
Would become:
# My Title
Created on 2022-04-06
(Bonus) Search Entries
Implement a search
command you can use to find files who’s file name contains the searched string.
./journal search "example"
/entries/example_file1.md
/entries/example_file2.md
You can use IO.puts/2
to print information in the console.
Commit Your Progress
Run the following in your command line from the project folder to track and save your progress in a Git commit.
$ git add .
$ git commit -m "finish journal cli exercise"