Journal CLI
Mix.install([
{:jason, "~> 1.4"},
{:kino, "~> 0.9", override: true},
{:youtube, github: "brooklinjazz/youtube"},
{:hidden_cell, github: "brooklinjazz/hidden_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.
Mark As Completed
file_name = Path.basename(Regex.replace(~r/#.+/, __ENV__.file, ""), ".livemd")
progress_path = __DIR__ <> "/../progress.json"
existing_progress = File.read!(progress_path) |> Jason.decode!()
default = Map.get(existing_progress, file_name, false)
form =
Kino.Control.form(
[
completed: input = Kino.Input.checkbox("Mark As Completed", default: default)
],
report_changes: true
)
Task.async(fn ->
for %{data: %{completed: completed}} <- Kino.Control.stream(form) do
File.write!(progress_path, Jason.encode!(Map.put(existing_progress, file_name, completed)))
end
end)
form
Commit Your Progress
Run the following in your command line from the curriculum folder to track and save your progress in a Git commit.
Ensure that you do not already have undesired or unrelated changes by running git status
or by checking the source control tab in Visual Studio Code.
$ git checkout solutions
$ git checkout -b journal-cli-exercise
$ git add .
$ git commit -m "finish journal cli exercise"
$ git push origin journal-cli-exercise
Create a pull request from your journal-cli-exercise
branch to your solutions
branch.
Please do not create a pull request to the DockYard Academy repository as this will spam our PR tracker.
DockYard Academy Students Only:
Notify your instructor by including @BrooklinJazz
in your PR description to get feedback.
You (or your instructor) may merge your PR into your solutions branch after review.
If you are interested in joining the next academy cohort, sign up here to receive more news when it is available.