Styling my RSS feed

This is a little xsl stylesheet I put together to make my RSS feed look nicer when viewed in the browser.

RSS feed with styles

Creating the basic feed stylesheet

The full stylesheet lives at https://www.philnewton.net/feed.xslt.xml, but the basics are below:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="html"/>

  <xsl:template match="/">
    <html>
      <head>
	<title>rss feed</title>
      </head>

      <body>
	<h1>
	  <xsl:element name="a">
	    <xsl:attribute name="href">
	      <xsl:value-of select="/rss/channel/link" />
	    </xsl:attribute>
	    <xsl:value-of select="/rss/channel/title"/>
	  </xsl:element>
	</h1>

	<div class="items">
	  <xsl:apply-templates select="/rss/channel/item" />
	</div>
      </body>
    </html>
  </xsl:template>

  <xsl:template match="item">
    <p>
      <xsl:element name="a">
	<xsl:attribute name="href">
	  <xsl:value-of select="link"/>
	</xsl:attribute>
	<xsl:value-of select="title" />
      </xsl:element>
      <br/>
      <span>
	<xsl:value-of select="pubDate" />
      </span>
    </p>
  </xsl:template>
</xsl:stylesheet>

The important parts are:

<xsl:template match="/">
This builds the body of the document. It adds a <h1> heading that links to the main site, along with a container for feed items.
<xsl:template match="item">
This displays individual items as post links with the date underneath.

Adding the stylesheet to the feed

The following line goes after the initial <?xml ?> declaration.

<?xml-stylesheet type="text/xsl" href="/feed.xslt.xml" ?>

Adding the feed title

The page title can be added dynamically from the feed by replacing the <title> element with the following:

<xsl:element name="title">
  <xsl:value-of select="/rss/channel/title"/>
</xsl:element>

Tidying up the appearance

This step is totally optional, but I wanted the feed to look a little more appealing. This goes inside the <head> element:

<style>
 html {
     background: #eee;
 }

 body {
     max-width: 64em;
     margin: 1em auto;
     font-family: monospace;
     font-size: 1.2em;
     background: white;
 }

 h1 {
     margin: 0;
     background: #f6f6f6;
     padding: 0.5em;
 }

 .items {
     padding: 1em;
 }

 span {
     color: #aaa;
     font-size: 0.8em;
 }

 a {
     color: #0047b5;
     border-bottom: 2px solid transparent;
     text-decoration: none;
     transition: all 150ms ease;
 }

 a:hover {
     color: #005be8;
     border-bottom: 2px solid #005be8;
 }
</style>

And finally, I added the following to the <head> element to make the feed mobile-friendly:

<meta name="viewport" content="width=device-width, initial-scale=1" />

I don't think that many people actually visit the feed directly, but with this change it's a little friendlier when they do.


Eight years of Beeminder

Beeminder has been part of my productivity arsenal for over eight years now. Even though that's a really long time to use a tool, it's still something that I struggle to explain. When I tell people that it's a service I pay money to when I don't get things done, the reaction is usually "why on Earth would you want to do that?!" I suppose that's fair.

The Beeminder team has written plenty of articles about the term "Akrasia" and how their software works to combat it. I treat it like corrective lenses; I don't exactly like wearing them, but they help me live my life. I'm not going to stop just because some people think they're weird.

Even though it's been a long time since I wrote it, my advice from "One year with Beeminder" still holds true. I try to automate as many goals as possible, and with few exceptions I don't use "time worked" for my goals.

One change I have made is to try and tie goals to an external data source.

For example, in 2014 I set myself the goal of releasing a game every month. Once I started derailing I resorted to entering dummy data instead of taking the losses. Because the data was limited to what I entered into Beeminder, I could get away with fudging the numbers without anyone knowing.

For new goals I will try to integrate things like repository commits, external API's, or habits that I track via org-mode.

My most successful goals so far:

Run a full marathon

I track all of my runs using RunKeeper, and then link that to a goal to track my miles run. Looking at the graph, it gets steeper around July 2019 when I started my half training. December 2019 has an even sharper increase, followed by a nice jump in March when I ran the full marathon.

There are a few jumps in the red line from when I set my goal to derail unless I ran that day. This was a handy tool for when I had miles built up and was contemplating ditching a training run.

Increase my earnings
My freelance earnings flat-lined towards the end of 2019, so I set up a goal to track my billed hours. It's a little weird, but it's kept me conscious of what I'm working on.
(Almost) write a book

When I started work on "Writing PHP with Emacs" I had a goal to track the number of words written. This was great for getting initial ideas out of my head, but it became less useful over time once I began editing things. I dropped this goal after two months.

After the initial release I stopped writing, which isn't the smartest way to get things done. Since then I've set up two goals: one to track when I publish new versions, and one to track when I make commits to the book's repository. Both of these together ensure that I'm writing and releasing things.

Exercise three times a week
I have an org-mode document that I use to track my workouts, and when a workout is completed it sends a datapoint to Beeminder. I considered tracking individual exercises (such as "do X pushups per week"), but this one has worked really well so far, and I think that tracking exercises could cause me to over train instead of derailing.
Do my GTD weekly review
In the past I struggled to stay on top of my weekly reviews. Since adding this goal I haven't missed a single one and it has made a big difference into how effective my GTD system is.

I've had plenty of derailments - Beeminder's term for failing to meet a deadline - but the value from staying on track far outweighs anything that I've lost.

Since I started I've also become more comfortable with creating short-term goals. Not everything has to be a life-changing habit, and these temporary goals often build into larger ones.

Beeminder is weird, but sometimes the risk of losing $5 is all it takes to get me across the finish line.


Sharing an Emacs configuration across machines

Once I started using Emacs more frequently, I wanted to use it on every machine I owned. But I found it a pain to keep the configuration synchronized between machines; there were always some little changes I'd need on my laptop and not my desktop (and vice versa). Preventing these changes from affecting the wrong machine became a headache, but thankfully there are ways around it with a little bit of elisp.

My current setup now uses the same configuration directory, but with custom setup files that only affect certain users and operating systems.

Here's how it all fits together.

no-littering

no-littering is a small package that keeps the ~/.emacs.d directory clean and organized. This makes it easier to exclude machine-specific files during synchronization.

It's available on melpa and doesn't require any configuration to get started; a single line is all that's required to use it:

;; Standard load.
(require 'no-littering)

;; or with use-package
(use-package no-littering)

Synchronization

I sync my ~/.emacs.d directory between machines using Dropbox. The following directories are excluded:

  • ~/.emacs.d/auto-save-list/
  • ~/.emacs.d/backup/
  • ~/.emacs.d/etc/
  • ~/.emacs.d/server/
  • ~/.emacs.d/var/

These directories often contain things that are tied to the current machine's directory layout, which can cause issues when they are synchronized. The backup directory in particular generates a lot of files and excluding that cut out a lot of headaches.

Per-user configuration

All of my per-user configuration lives in the ~/.emacs.d/users/{username}/ directory. All files in that directory are loaded last so that they override any default settings.

The following snippet is placed just before (provide 'init) in my ~/.emacs.d/init.el file.

;; Configure the user directory path.
(defvar user-settings-dir
  (concat user-emacs-directory "users/" user-login-name)
  "Settings directory for the current user.")

(defun pn/load-user-init-file (file)
  "Load an init file from FILE."
  (load (format "%s/%s" user-settings-dir file)))

;; Load all elisp files in the current user directory.
(when (file-exists-p user-settings-dir)
  (mapc #'pn/load-user-init-file
	(directory-files user-settings-dir nil "^[^#].*el$")))

Per-OS configuration

The final piece of the puzzle is setting up configuration that only runs on a specific operating system. I use this for setting fonts, paths, and themes.

I put this code in a user-specific init.el file, although it can also go at the end of the main init.el file.

(when (string= "windows-nt" system-type)
  ;; Windows only configuration.
  )

(when (string= "darwin" system-type)
  ;; Mac OS only configuraiton.
  )

(when (string= "gnu/linux" system-type)
  ;; Linux only configuration.
  )

Using all of these techniques together allows me to keep my Emacs configuration in sync with a minimum of fuss.


30 days of blogging kick off

One of my minor goals for 2020 is to try a 30 day trial of blogging every day. The first post on this site was published on June 8th, 2006 (5,254 days ago). Since then I've written 114 posts, which works out at one post every 46 days. I am not a prolific writer.

I've always wondered what would happen if I wrote more often. Would my writing improve? Would more people visit the site? Would I find inner peace? This is my way of finding out.

I am expecting this to be quite difficult, so I set up a goal on Beeminder to help keep me on track. Most of my beeminder goals have a week of mercy after failure, but this one does not, which means if I fail to publish a post I still need to write the next day. I've capped the charge at $5, but if I miss everything it would cost $150. Ouch.

My current list of post ideas has around twenty topics, but I'm expecting to come up with new ideas as I write. So if all goes well, in 30 days time this site will have 144 posts.


Groundhog Day Resolutions - October 2020

I didn't write my September post until the 21st, so it didn't give me much time to get things done. Here's how everything went.

September's Primary Goals

1. Complete at least one "other goal"

I marked "Contribute regularly to an online community" as completed. I've been doing this since the start of the year so I think it's fair to mark as completed.

It's a different experience from when I used to post on forums in the late 90's - a lot of communities are centralized on Reddit, Facebook, and Twitter - but it's been nice to talk to other people.

One thing I still want to do is integrate Webmention support into this site.

Out of all the goals I picked this year, this was probably the only one made easier by Covid restrictions.

2. Continue my bodyweight workouts

Not only did I not miss a single workout on September, but I also added some runs to the same days I did workouts. I was a little worried I wouldn't be able to continue the same workout schedule once marathon training starts, but so far things are looking good.

3. Create plans for my bodyweight skill goals, and follow them

I created a spreadsheet to track my progress through each set of progressions, and I added skill work to my scheduled workouts.

September's Secondary Goals

1. Prep for a 30 day blog marathon

I have a list of topics to write about, and I'm sure more ideas will surface once I start writing regularly.

2. Read a book!

I bought some books, but that doesn't count for much if I don't read them.

Primary Goals for October

1. Publish version 0.5 of Writing PHP with Emacs

I've been working on the latest chapter, "Using lsp-mode", for the last week or so. There's quite a lot of information to cover and it will probably be the largest chapter by quite a margin.

I'd like to have it published by the end of October.

2. Start blogging every day

Write every day for 30 days is one of my secondary goals for 2020. I want to have this started before November 1st rolls around.

3. Complete another secondary goal

With less than 3 months to go I need to make some serious progress here.

I have mixed feelings about September. I met nearly all of my targets, but I spent the first few weeks of the month focusing on work to the detriment of everything else. I don't like cramming goals in all at the end of the month - despite how frequently I do it - and it gave me a bit of an unpleasant feeling.

I'm deliberately keeping October's list of goals simple. I'm not a particularly good writer, so I want to make that my primary focus for the month.