Templates and Patterns

I’m real­ly nerdy. So nerdy in fact, that every one of my writ­ing projects, from active things to plot bun­nies I ignore after a week, has a main file that looks rough­ly like this:

\input{document-preamble}
\begin{document}
\input{document-gls}
\ifsubmission{
\input{synopsis}
} {
\pagestyle{empty}
\input{halftitle}
\clearpage
\input{other-works}
\cleardoublepage
\input{title}
\input{copyright}
\clearpage
\input{dedication}
\clearpage
\input{epigraph}
\cleardoublepage
\input{halftitle}
\cleardoublepage
\pagestyle{fancy}
\pagenumbering{arabic}
\setcounter{page}{3}
}
\input{part-A}
\input{part-B}
\input{part-C}
\input{part-D}
\ifsubmission {
\printglossary[type=special]
} {
\input{colophon}
}
\end{document}

There are a few cool things going on here.

  • Sub­mis­sion mode (which is for­mat­ted as a man­u­script due to some oth­er set­tings) includes a syn­op­sis, but non-sub­mis­sions don’t. Non-sub­mis­sion mode (which is type­set) includes the reg­u­lar front-mat­ter stuff like title, copy­right, and epi­graph pages.
  • In sub­mis­sion mode only, a list of all spe­cial char­ac­ters gets includ­ed at the end; non-sub­mis­sion builds instead have a colophon.
  • The actu­al sec­tions of a project are bro­ken into parts (in this case, parts A, B, C, and D. It’s not shown here, but I can also build just those sec­tions of a project if I want to focus on edit­ing a spe­cif­ic sec­tion. Since I use part des­ig­na­tors for major sto­ry events (not nec­es­sar­i­ly parts of a three-act struc­ture), the num­ber of these can vary.
  • Stuff that’s com­mon for all the doc­u­ments I’ll gen­er­ate (e.g., the part doc­u­ments men­tioned above and sto­ry bible) is kept in exter­nal files (doc­u­ment-pre­am­ble and doc­u­ment-gls).

All in all, I can effec­tive­ly copy this doc­u­ment and use it for any project, only mod­i­fy­ing the num­ber of part-* files. At the end of the day, it’s triv­ial to give even the most flim­sy plot bun­ny all the bells and whis­tles (and see­ing a doc­u­ment type­set well acts as encour­age­ment).

Because the above snip­pet is so sim­i­lar across projects though, what if I could avoid copy­ing it every­where and have tool­ing gen­er­ate it? Since the only parts that real­ly need to change are the parts I’m inputting, sure­ly I can knock out a half-assed script to build every­thing.

Real­ly, that file only has to exist to make LaTeX hap­py. In fact, lots of files only need to exist for the sake of build­ing, and their actu­al con­tents are pret­ty irrel­e­vant. The title and half title pages for exam­ple, will be the title and my name. The copy­right page has a ton of infor­ma­tion, but most of it is (again) copy and past­ed. Oth­er works (when I actu­al­ly have them mind you) could eas­i­ly just be a list that’s sta­t­ic across every project.

The only parts that are actu­al­ly unique in every project:

  • the title (in the pre­am­ble file)
  • the glos­saries (doc­u­ment-gls)
  • the syn­op­sis
  • the epi­graph and ded­i­ca­tion pages
  • the main mat­ter (part-{A,B,C,D})

Because I’m using CMake to build every­thing, I decid­ed to just have CMake gen­er­ate the com­mon stuff for me; now it’s not even clut­ter­ing up my git his­to­ry. In fact, the cre­ation func­tion is pret­ty sim­ple:

include(create_target)
set(CREATE_FULL_DIR "${CMAKE_CURRENT_LIST_DIR}")
function(create_full codename parts number tex_files)
foreach(part IN LISTS parts)
set(BOOK_PARTS "${BOOK_PARTS}\n\input{part-${part}}")
endforeach()
  set(BOOK_CODENAME ${codename}) set(BOOK_NUMBER ${number}) configure_file("${CREATE_FULL_DIR}/halftitle.tex" "${CMAKE_CURRENT_BINARY_DIR}/halftitle.tex" COPYONLY ) configure_file("${CREATE_FULL_DIR}/full.tex.in" "${CMAKE_CURRENT_BINARY_DIR}/${codename}-full.tex" @ONLY ) configure_file("${CREATE_FULL_DIR}/title.tex.in" "${CMAKE_CURRENT_BINARY_DIR}/title.tex" @ONLY ) # These will create targets for building configure_file("${CREATE_FULL_DIR}/submission.tex.in" "${CMAKE_CURRENT_BINARY_DIR}/${codename}.tex" @ONLY ) create_target(${codename} "${tex_files}" TRUE) configure_file("${CREATE_FULL_DIR}/pub.tex.in" "${CMAKE_CURRENT_BINARY_DIR}/${codename}-pub.tex" @ONLY ) create_target("${codename}-pub" "${tex_files}" FALSE) # alternate layouts foreach(layout IN ITEMS "lulu") set(LAYOUT_STYLE ${layout}) configure_file("${CREATE_FULL_DIR}/pub-alt.tex.in" "${CMAKE_CURRENT_BINARY_DIR}/${codename}-${layout}.tex"
@ONLY ) create_target("${codename}-${layout}" "${tex_files}" FALSE) endforeach()

endfunction()

Now all my projects need to do is add a few lines to their CMakeLists.txt and I get all this beau­ti­ful­ness hap­pen­ing auto­mat­i­cal­ly.

include(create_full)
# ...
create_full("wolf" "A;B;C;D" "I" "${tex_files}")

So why is all this worth the trou­ble though? Well, for one humans are real­ly good at work­ing with pat­terns; it’s why some­things you just know that some moron on the road is going to cut you off, or that your dog got into some­thing she wasn’t sup­posed to, because you picked up on tiny details you weren’t active­ly observ­ing but have learned to rec­og­nize and react to. If you want a prac­ti­cal exam­ple, go play any of the Mega Man games and see how long it takes before you can beat one of the boss­es with­out any chance of dying (Chill Pen­guin in Mega Man X is a good place to start).

Now I’ve man­aged to pat­tern the basics of every writ­ing project I have; all the lit­tle things that should be iden­ti­cal across prod­ucts (fonts, for­mat­ting, the order of front and back mat­ter) are the same. The stuff that’s actu­al­ly in a project repos­i­to­ry on the oth­er hand is actu­al­ly unique to the project with only min­i­mal boil­er­plate (basi­cal­ly enough to lever­age the CMake helper stuff).

But does any of this real­ly mat­ter? Oth­er than peo­ple who want to nerd out on automa­tion and gen­er­at­ing files, is their a prac­ti­cal pur­pose to using pat­terns like this?

Of course.

I’ve been sit­ting on some san­i­ty-check­ing scripts for a while, most­ly to help find fil­ter words and stuff like that. I’ve nev­er both­ered to auto­mate them pre­cise­ly because when I wrote the scripts, each of my projects was a spe­cial snowflake that’d require unique work and I’m way too lazy for that. Now that my projects look the same…it’s bor­der­line triv­ial to cre­ate a small suite of scripts to run the stuff I’ve already writ­ten against any­thing with the expect­ed project struc­ture.

Not only that, but I’m a fan of automat­ing basic tasks; now I can add more Jenk­ins jobs since I know what to expect. In fact, the jobs I use already depend on hav­ing some basic things in com­mon. Using tools to fill in the com­mon parts for my projects is sim­ply a more effi­cient method than copy­ing and tweak­ing files on a per-project basis.

Leave a Reply

Your email address will not be published. Required fields are marked *