Case Study: Building 'mdd'
A real-world example: building a Markdown tool ("mdd") to manage blog posts. Because hugo new is great, but we want more.
Step 1: Initialize the Project
We start by creating a full project structure with a subcommand article.
>
parseArger project mdd \ --description "markdown tools for my blog" \ --git-repo "DimitriGilbert/mdd" \ --project-subcommand article
This creates the mdd folder, bin/article, utils/install, and more.
Step 2: Define the Arguments
We need a lot of metadata for our blog posts: title, categories, tags, series, date, etc.
>
parseArger parse bin/article \ --pos 'title "article title"' \ --opt 'folder "article folder name" --alias directory --alias dir' \ --opt 'categories "article parent categories" --short c --alias cat --alias parent --repeat' \ --opt 'tags "article tags" --repeat --short t --alias tag' \ --opt 'series "article belongs to this series" --alias group --short g' \ --opt 'date "publication date meta" --alias publication --alias publish-at --short d' \ --opt 'summary "summary meta" --short s --alias description --alias desc' \ --opt 'template "template file to use" --alias tpl' \ --opt 'headings "add headings to your file" --repeat --alias part --alias h2' \ --opt 'headings-level "heading level" --default-value 2 --alias hl' \ --flag 'draft "is it a draft" --on --no-alias publish' \ --nested-options 'meta "add any meta you want"' \ --in-place
Step 3: The Implementation
Now we write the logic in bin/article using the variables ParseArger generated for us.
Handling Folders
_container_dir="$_arg_title";
if [ "$_arg_folder" != "" ]; then
_container_dir="$_arg_folder";
fi
# Handle categories as path
if [ "${#_arg_categories[@]}" -gt 0 ]; then
_cat_dir="";
for _cat in "${_arg_categories[@]}"; do
_cat_dir+="$_cat/";
done
_container_dir="$_cat_dir$_container_dir";
fi
if [ ! -d "$_container_dir" ]; then
mkdir -p "$_container_dir";
fiGenerating Frontmatter
_metas_str="title: $_arg_title\n";
if [ "$_arg_date" != "" ]; then
_metas_str+="date: $_arg_date\n";
fi
if [ "${#_arg_tags[@]}" -gt 0 ]; then
_metas_str+="tags: \n";
for _tg in "${_arg_tags[@]}"; do
_metas_str+="\t- $_tg\n";
done
fi
if [ "$_arg_draft" == "on" ]; then
_metas_str+="draft: true\n";
fi
# Handle nested options
if [ "${#_arg_ns_meta[@]}" -gt 0 ]; then
for _tmp_k_meta in "${!_arg_ns_meta[@]}"; do
_metas_str+="$_tmp_k_meta: ${_arg_ns_meta[$_tmp_k_meta]}\n";
done
fiGenerating Headings
if [ "${#_arg_headings[@]}" -gt 0 ]; then
_hd_level_str="";
for (( i=0; i<_arg_headings_level; i++ )); do
_hd_level_str+="#";
done
for _hd in "${_arg_headings[@]}"; do
_contents_str+="\n$_hd_level_str $_hd\n\n\n";
done
fiStep 4: Refinement (The "Oops" moment)
We forgot a way to overwrite existing files. Let's add a force flag.
>
parseArger parse bin/article -i --flag 'force "erase if exists"'
And update our logic:
if [ "$_arg_force" == "on" ] && [ -f "${_container_dir}/index.md" ]; then
rm "${_container_dir}/index.md" -f;
fiStep 5: Polish
Generate documentation and completion scripts so we don't hate ourselves later.
>
parseArger document --file ./mdd --directory ./bin --title "MarkDown for Didi" > documentation.md
>
parseArger completely "mdd" ./mdd --subcommand-directory ./bin --no-run-completely > completely.yaml completely preview > completely.bash
Setting up the Environment
Finally, we create an mdd.rc file to source our new tool and its completion into our shell.
if [ "${MDD_DIR}" != "" ]; then
alias mdd="${MDD_DIR}/mdd";
[ -f "${MDD_DIR}/completely.bash" ] && source "${MDD_DIR}/completely.bash";
fi