A First Post!

1. Introduction

Hello, world! Welcome to my new blog. I plan to use this blog to document technical tips, hacks, bugs, coding “best practises,” and the like. Like many programmers, I spend heaps of my day, not implementing features, but trying to get a constellation of libraries, services, tools, and code to play nicely together. Normally, once I fix something, I immediately get back to whatever I’m supposed to be doing; however, I think it would be much more interesting to collect these in a blog!

Aesthetically-speaking, this blog is a bit ugly, but I plan to keep it that way. The entire site is generated automatically (via org-mode) from my plain-text notes. My goal is to brain-dump as fast an effortlessly as possible, so I’m going to force myself to not waste time making things pretty.

2. Content

I plan to write two types of documents on this site:

articles
These are normal documents, about a specific topic. These files are sorted into a topic-based directory structure. I may or may not return to update individual articles as I learn more about their topics.
blog posts
These posts are journal/diary/log entries. They’re sorted by date, with the most recent coming first. I’ll probably use these to rant and rave about whatever is on my mind at the time.

3. Navigation

Every article and post (like this one) is divided into numbered sections. These sections can be navigated to from the Table of Contents, in the sidebar.

The sidebar also contains a site-map linking to every article and post on the site. There’s also a JavaScript widget to help quickly filter through the site-map. When this widget has focus, you can press enter to navigate to the top-most entry.

4. Example widgets

Here I’ll list some example widgets that can easily be created with org-mode. This is mostly for my benefit. Until that muscle-memory kicks in, I can use this as a reference! 😁

4.1. Quotations

For stealing other people’s words. It seems that org-mode doesn’t generate <cite> elements when exporting to HTML. That will remain a TODO for now.

Update: It’s not built-in, but it’s easy enough to generate your own <cite>:

#+begin_quote
  */Magit/* is a complete text-based user interface to Git.

  #+begin_cite
    [[https://magit.vc/][Magit Homepage]]
  #+end_cite
#+end_quote

Magit is a complete text-based user interface to Git.

Magit Homepage

4.2. Notes

I expect to use these for more-important tid-bits of information. Warnings perhaps.

  • As of org-mode 9.2, “easy templates” (like <s) have been replaced by C-c C-,. org-mode provides org-tempo.el to reimplement this functinality, but for now I’m using YASnippets instead.
  • Another note.

4.3. Images

floppy-care.gif

4.4. Source code

Here are a few example code blocks. I expect to include a lot of code examples in my posts.

4.4.1. Emacs Lisp

1: (defun dired-xdg-open-file ()
2:   "In dired, open the file named on this line."
3:   (interactive)
4:   (let ((file (dired-get-filename nil t)))
5:     (call-process "xdg-open" nil 0 nil file)))

4.4.2. JavaScript

/**
 * This component loads the generated /sitemap file and displays it inline.
 */
export default
class Sitemap extends Component {
  constructor(props) {
    super(props)

    this.state = {
      error: null,
      isLoaded: false,
      items: JSON.parse(localStorage.getItem(storageKey) || '{}'),
      filter: '',
      filterItems: {},
    }

    this.listRef = React.createRef()
    this.patternCache = this.createPatternCache()

    this.activePath = ['org.ertt.ca'].concat(
      document.location.pathname.split("/").filter(part => part.length)
    )
  }
}

4.4.3. C

char* APP_NAME;

int main(int argc, char *argv[])
{
    APP_NAME = argv[0];

    if (argc < 2) {
        fprintf(stderr, "No command specified\n");
        fprint_usage(stderr, APP_NAME);
        return ERR_ARGS;
    }

    const char* CMD = argv[1];
    argc -= 2;
    argv += 2;
    switch(parse_command(CMD)) {
        case CMD_BITMAP: return cmd_bmp(argc, argv);
        case CMD_FONT:   return cmd_font(argc, argv);
        case CMD_FORMAT: return cmd_fmt(argc, argv);
        case CMD_HELP:
            fprint_usage(stdout, APP_NAME);
            return 0;
        default:
            fprintf(stderr, "Unknown command: %s\n", CMD);
            /* fprint_usage(stderr, APP_NAME); */
            return ERR_CMD_UNKNOWN;
    }
}

4.4.4. ruby

module Blog
  VERSION = '1.0.0'.freeze

  def self.version
    if (tag = ENV['TAG'])
      "#{VERSION}-#{tag}"
    else
      VERSION
    end
  end
end

4.4.5. HTML

<!doctype html>
<html lang="en">
<head>
  <title>HTML!</title>
  <link rel="stylesheet" href="css/styles.css">
</head>
<body>
  <script src="js/scripts.js"></script>
</body>
</html>

4.4.6. CSS

mark {
  background: #ff0;
  color: #000;
}
audio:not([controls]) {
  display: none;
  height: 0;
}
q {
  quotes: "\201C" "\201D" "\2018" "\2019";
}

4.4.7. SQL

SELECT customerName, customercity, customermail, salestotal
  FROM onlinecustomers AS oc
  JOIN orders AS o ON oc.customerid = o.customerid
  JOIN sales  AS s ON o.orderId     = s.orderId

4.4.8. Nix

{ nixpkgs
, default-system ? {}
, ...
}:

let
  inherit (nixpkgs.lib) head tail isAttrs isList unique zipAttrsWith;

  bothAttrs  = l: r: isAttrs r && isAttrs l;
  bothLists  = l: r: isList r && isList l;
  mergeLists = l: r: unique (l ++ r);

  merge = lhs: rhs:
    let
      f = attrPath:
        zipAttrsWith (n: values:
          let
            here = attrPath ++ [n];
            l = head values;
            r = head (tail values);
          in
            if tail values == []
            then l
            else
              if bothAttrs l r
              then f here values
              else (if bothLists l r then mergeLists l r else l)
        );
    in f [] [rhs lhs];

  nixosSystem = args: nixpkgs.lib.nixosSystem (merge default-system args);
in
{
  inherit nixosSystem;
}

4.4.9. Haskell

module Y2019.Day01 (parts) where

parts = ( (part1, Just "3152919")
        , (part2, Just "4726527")
        , readMasses
        )

part1 :: [Int] -> String
part1 masses = show . sum $ fuelRequirement <$> masses

part2 :: [Int] -> String
part2 masses = show . sum $ totalRequirement <$> masses

readMasses :: String -> [Int]
readMasses = fmap read . lines

fuelRequirement :: Int -> Int
fuelRequirement mass = (floor $ fromIntegral mass / 3.0) - 2

totalRequirement :: Int -> Int
totalRequirement mass | fuel <= 0 = 0
                      | otherwise = fuel + totalRequirement fuel
  where fuel = fuelRequirement mass

4.4.10. rust

mod logging;

use init_ai::game_loop;
use stdweb::js;

fn main() {
    if cfg!(feature = "debug") {
        logging::setup_logging(logging::Debug);
    } else {
        logging::setup_logging(logging::Info);
    }

    js! {
        const game_loop = @{game_loop};

        module.exports.loop = () => {
            // Provide actual error traces.
            try {
                game_loop();
            } catch (error) {
                // console_error function provided by 'screeps-game-api'
                console_error("caught exception:", error);
                if (error.stack) {
                    console_error("stack trace:", error.stack);
                }
                console_error("resetting VM next tick.");
                // reset the VM since we don't know if everything was cleaned up
                // and don't want an inconsistent state.
                module.exports.loop = wasm_initialize;
            }
        };
    }
}

4.4.11. go

  func main() {
        dbFilename := os.Getenv("BGNOISE_DB")
        if len(dbFilename) == 0 {
                dbFilename = database.DefaultFilename
        }

        db := database.MustConnect("sqlite3", dbFilename)
        defer db.Close()

        migrator := database.Migrator{DB: db}
        migrator.Execute()

        if len(os.Args) < 2 {
                commands.PrintHelpAndExit(os.Stderr, 2)
        } else {
                runCommand(db, os.Args[1], os.Args[2:])
        }
  }

4.4.12. C#

public void Main(string arg, UpdateType updateType)
{
    if ((updateType & ManualUpdate) != 0) {
        _closer.Toggle(arg);
    } else if ((updateType & FrequencyUpdate) != 0) {
        _closer.Update();
    }
}