Skip to content

Commit

Permalink
[pta_workshop] Adds a ton
Browse files Browse the repository at this point in the history
  • Loading branch information
colindean committed Jul 18, 2020
1 parent da86f1e commit c1725cf
Show file tree
Hide file tree
Showing 8 changed files with 265 additions and 6 deletions.
5 changes: 4 additions & 1 deletion intro_to_plaintextaccounting/workshop/0100_dependencies.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Install Dependencies
# Install Dependencies {#sec:dependencies}

Let's install some software that you'll need in order to work through this workshop. It's easiest to install the basics upfront so that you can continue the workshop even if you may be offline later.

Expand Down Expand Up @@ -59,6 +59,7 @@ brew 'ledger'
brew 'entr'
brew 'xsv'
brew 'python'
brew 'make'
```

### Python's `pip`
Expand Down Expand Up @@ -100,5 +101,7 @@ echo "# xsv"
xsv --version
echo "# python"
python --version
echo "# make"
make -v
```

19 changes: 14 additions & 5 deletions intro_to_plaintextaccounting/workshop/0250_basic_automation.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,18 @@ reports.

## Thinking in Reports

## A Brief Introduction to `make`

## Setting Up a `Makefile` for use with `ledger`

## Solidifying Report Generation
When using `ledger` and other plain text accounting tools, think in reports:
the results of a query of data presented in a particular format.
Each report has meaning and reusability.
So far, you've read about four different kinds of reports that are the most common for basic personal finance:

1. A generalized or account-scoped balance report
2. A generalized or account-scoped register report
3. A paired short-term balance report: cash flow (@sec:cashflow)
4. A paired long-term balance report: net worth (@sec:networth)

Each of these is comprised of a single query and a single output.
If we put the output of each of these queries into a file, we
can use and reuse these files to create a really cool unified report
comprised of these and other reports.

66 changes: 66 additions & 0 deletions intro_to_plaintextaccounting/workshop/0251_make_intro.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
## A Brief Introduction to `make`

`make` is a file-oriented build automation tool.
A `Makefile` describes the steps necessary to create a file in the form of
a _task_.
A task declares an output and several inputs, then defines the steps necessary
to create the output file.
While tasks are generally files, tasks can also be _phony_, in that
they don't actually create the file named in the task.
@Lst:example_makefile shows a basic `Makefile`.

Listing: A basic `Makefile` used to compile a program written in C {#lst:example_makefile}

```{.makefile}
# get all of the C files in the directory
C_FILES := $(wildcard *.c)
# replace the 'c' with 'o', convention for compiled C object code
OBJECTS := $(patsubst %.c, %.o, $(C_FILES))
# the final binary file name
BINARY := hello
# convenient way to run, and the first task defined is the executed when
# running make without a specified task
all: $(BINARY)
# to produce $(BINARY), we need all $(OBJECTS) to be built
$(BINARY): $(OBJECTS)
# $@ means "the output file"
# $^ means "all of the input files"
$(CC) $(LDFLAGS) -o $@ $^
# to produce any file ending in '.o', we need a file that has the same
# basename but ends with '.c'. These files already exist, so make tracks
# when they've been altered and knows that it only needs to run this for
# altered files.
%.o: %.c
# $< means "just the first input file"
$(CC) $(CFLAGS) -c -o $@ $<
# phony explicitly declares that this task will never create a file.
.PHONY: clean
clean:
rm -rf $(BINARY) $(OBJECTS)
```

`make` is one of the older tools still in active use today.
Designed by Stuart Feldman in 1976, the three common derivatives – BSD make,
GNU make, and Microsoft nmake (@wikipedia:make).
It's important to know what version of `make` you are using because there are
subtle differences between these derivatives.
If you are using Linux, you are likely using GNU make.
If you are using macOS or a BSD, you are likely using BSD make by default, but
can install GNU make easily.
If you are using Windows, you likely have nmake available to you if you have
installed Visual Studio or you can install GNU make through scoop.
You probably installed GNU make in the process of installing the dependencies
of this workshop in @sec:dependencies.
Run `make -v` to see the version, and ensure that it's the same derivative,
but not necessarily the same _version number_ what's shown in @lst:versions.
If it's not, you'll _probably_ be OK as this author tries to write portable
Makefiles [^portability], but you've been warned.

[^portability]: Derivatives of `make` have different ways of handling tabs
versus spaces and provide some different built-in functions.
GNU make tends to be the most permissive and user-friendly,
thus this author's preference for it in general.
165 changes: 165 additions & 0 deletions intro_to_plaintextaccounting/workshop/0252_make_for_ledger.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
## Setting Up a `Makefile` for use with `ledger` {#sec:make_for_ledger}

In this section, you will read prose and in-line comments to learn about `make`
and `ledger` at the same time, and maybe some other helpful utilities along the
way.

Note that this `Makefile` you are building relies on following a hierarchy of
accounts matching what this author uses for his personal finances. This
`Makefile` is nearly identical to what he uses. It has been modified only
for clarity and actual useful improvements that he should have done long ago!

### Using Make Instead of a Script

Revisit the simple script in @lst:simple_script.
This approach is fine, but for benefits we'll _really_ see in @sec:parallelism,
you must to implement some helpful tasks in in a `Makefile` instead of a
simple shell script.

A convenient way pass commands into `make` when running it is to use
environment variables specified at invocation.
For example,
`make report YEAR=2019` would run the `report` task after setting the `YEAR`
variable during startup. You'll use this mechanism to override some default
values that you'll specify at the top of the `Makefile`.

Listing: Some base variables for `Makefile` {#lst:makefile_variables}

```{.makefile pipe="tee Makefile.01.vars.txt" .numberLines}
# The year for reports tied to a year, defaults to the current year.
# Unlike shell scripting, where this would be $(date) or `date`,
# Makefile provides a special function $(shell cmd) to run a command.
YEAR = $(shell date +%Y)

# The path to the ledger binary. In general, it'll be on your $PATH
# but if you want to use a different ledger binary or perhaps use it
# provided within a Docker container, you'll need a way to override it.
LEDGER_BIN = ledger

# It's a common practice to put all transactions into one file per year
# and use the `equity` command from the previous year to start off the
# new year file
LEDGER_FILE = $(YEAR).ledger

# This combined variable provides a convenient way to execute ledger
# with the file already populated, plus a convenient way to inject
# eXtra arguments into the command as a one-off. This is a great way
# to test new commands.
LEDGER = $(LEDGER_BIN) -f $(LEDGER_FILE) $(X)

```

The `Makefile` variables created in @lst:makefile_variables are a great
starting point.
You will inenvitably forget the meaning and utility of some of the tasks
you're about to create, so let's use a fantastic way of documenting the
`Makefile`: creating a "doc comment" that a task can parse out of the
file and display. While you're at it, create another task that will help you
open the year's file without having to remember – or think about – what year it is.
The 2020s have been a a long decade for all of us!

Listing: Helpful tasks for your `Makefile` {#lst:makefile_help}

```{.makefile pipe="tee Makefile.02.help.txt" .numberLines}
### Help Tasks

help: ## Prints help for targets with comments
@grep -E '^[a-zA-Z._-]+:.*?## .*$$' $(MAKEFILE_LIST) | \
sort | \
awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'

edit: $(LEDGER_FILE) ## opens the transaction log for the year in your text editor
$(EDITOR) $(LEDGER_FILE)

## end help tasks
```

Next, you'll add some basic tasks that generate some reports for display in the
terminal.
Terminal-focused reports are great for quick checks or general use.
Some plain text accounting practitioners never really go past this or skip
directly to a graphical tool like you will learn about in @sec:fava.
Add the contents of @lst:makefile_terminal to your `Makefile`.

Listing: Basic business tasks for viewing common reports {#lst:makefile_terminal}

```{.makefile pipe="tee Makefile.03.terminal.txt" .numberLines}
### Terminal Viewing Tasks

bal: $(LEDGER_FILE) ## show all balances
$(LEDGER_CMD) balance

networth: $(LEDGER_FILE) ## show short net worth report
$(LEDGER_CMD) --depth=2 balance ^Assets ^Liabilities

networth-all: $(LEDGER_FILE) ## show complete net worth report
$(LEDGER_CMD) balance ^Assets ^Liabilities

cashflow: $(LEDGER_FILE) ## show cashflow report
$(LEDGER_CMD) balance ^Income ^Expenses

expenses: $(LEDGER_FILE) ## show non-paycheck expenses (no taxes or health insurance)
$(LEDGER_CMD) balance ^Expenses and not ^Expenses:Taxes and not ^Expenses:Insurance

checklist: $(LEDGER_FILE) ## show a list used to check accounts
$(LEDGER_CMD) accounts ^Assets:Cash ^Liabilities

raw: $(LEDGER_FILE) ## run a query with make raw Q="bal" or drops into console mode
$(LEDGER_CMD) $(Q)

cash: $(LEDGER_FILE) ## show only cash assets
$(LEDGER_CMD) balance ^Assets:Cash

## end terminal viewing tasks
```

Now that you've got a lot of tasks in your `Makefile`, run `make help` to see
the help text associated with each tasks.
The doc comment which has two octothorpes [^octothorpe] becomes the help text.

Listing: The output of `make help` so far {#lst:makefile_output_help}

```{pipe="bash" .numberLines}
cat Makefile.*.txt > Makefile.help
make -f Makefile.help help | aha | pandoc -f html -t plain
```

**PROTIP:** Always remember to leave helpful comments in your code: you are helping the
next person to read the code or documentation understand what you meant, and
it's more than likely that the next person will be yourself.

[^octothorpe]: Really, that's its real name, but it's commonly called a
"hash" or "hashtag" or "pound" or "number". Musicians might call it a "sharp"
in a musical context. Try injecting _that_ into a technical conversation
sometime.

Let's examine some of these tasks beyond their help text.

Finally, your `Makefile` should look like @lst:makefile_final.

Listing: The finalized basic `Makefile` for a `ledger` project {#lst:makefile_final}

```{.makefile pipe="cat Makefile.*.txt | tee Makefile.basic" .numberLines}
```

### Report File Generation

#### Parallelism {#sec:parallelism}

One of the most powerful features of `make` is its ability to build an internal
[directed acyclic graph](https://en.wikipedia.org/wiki/Directed_acyclic_graph)
of tasks to be executed and then execute those tasks in parallel where
possible.
This parallel execution is particularly useful when the production of a single
build artifact – a program – is actually the result of combining several
smaller products.

This sounds familiar, no? It is the same as what you are doing in building
reports with your `ledger` transaction log.
For one business for which this author handles finances, the difference between
running `make statement`, which runs all reports and builds the final document
serially, and `make -j 8 statement`, which runs reports in parallel before
building the final document, is dozens of seconds.
As the transaction log grows over time, the savings will only get bigger!


Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
## Solidifying Report Generation
3 changes: 3 additions & 0 deletions intro_to_plaintextaccounting/workshop/0999_references.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## References {#sec:references}

Contents from these works were used in the production of this workshop.
5 changes: 5 additions & 0 deletions intro_to_plaintextaccounting/workshop/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,17 @@ variables:
#include-in-header:
# - headers.tex

bibliography:
- refs.bibtex

number-sections: true

pdf-engine: xelatex
filters:
- pandoc-crossref
- pandoc-citeproc
- pandoc-include-code
# - filter-pipe.lua
- panpipe

preserve-tabs: true
7 changes: 7 additions & 0 deletions intro_to_plaintextaccounting/workshop/refs.bibtex
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@misc{wikipedia:make,
author = "Wikipedia",
title = "{Make (software)} --- {W}ikipedia{,} The Free Encyclopedia",
year = "2020",
howpublished = {\url{http://en.wikipedia.org/w/index.php?title=Make\%20(software)&oldid=966907354}},
note = "[Online; accessed 17-July-2020]"
}

0 comments on commit c1725cf

Please sign in to comment.