A long time ago I organized a study group to read the whole RI state budget. We were lucky to get Tom Sgouros to guide us through this massive document. (At the time there was no online version so we got printed copies. I remember struggling to carry the weight of multiple copies of its multiple volumes as I walked to my car.) Anyway, one of the things we learned was that DOT has almost no debt service. How can a $981M department that is responsible for roads, bridges, etc with lots of bond money projects have only $330K of debt service? It achieves this by hiding it within the Department of Administration. Most of the DOA's $211M debt service is actually DOT's. DOT costs Rhode Islander's well over a billion dollars a year. I honestly don't know if this cost is outrageous, or if it is money well spent. But it is useful to know the scale of the effort to build and maintain the road infrastructure.
SSL terminating tunnel using ghostunnel
From time to time the need for a simple SSL terminating tunnel is wanted. This is used to enable the browser to use an HTTPS connection to an HTTP server. It is common to use a proxy server, but I was curious if there was something simpler. I was able to create an SSL tunnel using ghostunnel
https://github.com/ghostunnel/ghostunnel
To build it for MacOS 14.7 I needed to update the go.mod to use toolchain go1.22.7
(instead of toolchain go1.22.4
).
Created the cert and key
openssl req \ -x509 \ -newkey rsa:4096 \ -keyout key.pem \ -out cert.pem \ -sha256 \ -days 3650 \ -nodes \ -subj "/C=US/ST=RI/L=Providence/O=MojoTech/OU=Labs/CN=clientsite.com"
Add the client's domain name to /etc/hosts
127.0.0.1 clientsite.com
Run the tunnel
sudo ghostunnel server \ --listen clientsite.com:443 \ --target localhost:3000 \ --cert cert.pem \ --key key.pem \ --disable-authentication
Run Python's file directory serving http server
python3 -m http.server 3000
And finally, open https://clientsite.com in the browser or with curl
curl -k https://clientsite.com
I think since this is Go and executables are statically linked, you could share the ghostunnel executable and PEMs with other developers.
Bye little Linode VM
The website https://andrewgilmatin.com/ is no more. I wasn't using the little Linode VM for much of anything anymore. If I were to keep it running I really needed to move it off of the discontinued CentOS 7. I would have to transition content, old code, and figure out security. Much has changed since I last needed to do that. I was not up for that marathon again.
Sensitive side of pure evil
I am reading Lord of the Rings for the first time. Yes, reading LotR is a right of passage for geeks, but I'm really only a geek by circumstances rather than by anything deeper. (I have watched Peter Jackson's movies several times, if that helps.) I am enjoying the books, having starting with the Hobbit. But several times I have wondered how a young reader today, one not raised in bucolic Devon, responds to Tolkien's beautifully rendered landscapes? Those landscapes are integral to the book and, for me, a sustaining attraction.
I did try watching the first season of the Rings of Power, but quickly gave up. Others have well explained its many, many failures. It is now in its second season and, apparently, has very strange things to say about the sensitive side of pure evil.
Rings of Power’s orc baby: Amazon’s Lord of the Rings prequel doesn’t get it right. | Vox
Ad hoc systems for managing work
I love seeing people's systems for managing their work. Even those of fictional people. This short from The Bear on managing the restaurant's guests and their orders is great.
A templating system using the file system for inheritance
Way back in the early days of the web, around 2004, I wrote a templating system that used the file system for inheritance. I think Fred Toth originally conceived of the technique.
In the directory /A/B/C
you place the template M
with content
Hello [%include N%]
You then have the templating system expand /A/B/C/M
. It would execute the directive [%include N%]
to include the template N
by looking up the directory tree, in order, /A/B/C/N
, /A/B/N
, and /A/N
, and using the first N
it found. You would place common templates (eg headers) and default content (eg company name) in the upper directories and "override" them in the lower directories. It worked really well for the mostly static sites my department was creating.
I have not seen something like this elsewhere. You can, however, achieve the same effect by manipulating your templating system's template search path per output document.
The system came to be called Trampoline and it has a Perl and a partial Java implementation. The implementations are in the Clownbike project at Source Forge. None of the templates Clownbike used made it to Source Forge, unfortunately. Those became the proprietary web sites our customers were paying for. Galley, an internal project, seems to have some.
I have no idea if any of this code still works. I am sure to be embarrassed by the code's quality! Some quiet, rainy day this winter perhaps I will try running it.
Red Indian Pipes
On a walk this weekend I saw a red Indian Peace Pipe. Neither I or my wife had ever seen one before. Apparently, they are not common, but also not rare.
Setting a Mac's default email client
I mostly love using Macs, but sometimes the conviences provided are not. I needed to change my default mail client to Microsoft Outlook. You set the default mail client within Apple's Mail app's Settings. However, you can't access Settings unless you first configure an email account! Since I don't want Mail to touch a actual real email account I ran these mail services locally using Docker:
docker run virtuasa/docker-mail-develThis enabled me to configure Mail to use "debug@example.com" and the local POP server. And now I can access Mail's Settings to set the default mail client to Microsoft Outlook. I really do feel for all those users without there own System Admin.
MSCHF's “Not Wheels” and Osprey's Gaslands
Examples of hardiness
Using data from external files
I am working with some code that processes CSV files. Each row corresponds to an existing record and the record is updated in response to the column values. This is not an uncommon task. The existing code implements this in an also not uncommon way by intermixing row parsing and record updating. For example, assume we are updating Foos
class Foo attr id, :location, :valuation # ... endA typical intermixing is
row = [...] raise "unusable foo id" if row[0].blank? foo = Foo.find(row[0].to_i) raise "foo not found with id #{row[0]}" unless foo raise "unusable town location" if row[1].blank? location = Location.find_by(town: row[1]) raise "location not found with town #{row[1]}" unless location; foo.location = location raise "unusable valuation" unless values[2].to_i < 10_000 foo.valulation = values[2].to_iWhile this initially seems like a reasonable approach it quickly breaks down as the number of columns increase, column format is non-trival, and there are column (or row) interdependencies. But the more significant problem is that the parsing and the updating can't be tested individually. This makes the test harder to write, understand, and maintain.
It is always better to first parse the raw data, validate it, and then use it. Eg
class Record attr :id, :town, :valuation attr :foo, :location def initialize(values) raise "unusable foo id" unless /^\s*(\d+)\s*$/ =~ values[0] id = $1.to_i raise "unusable town location" unless /^\s*(.+)\s*$/ =~ values[1] location = $1 raise "unusable valuation" unless /^\s*(\d+)\s*$/ =~ values[2] valuation = $1.to_i end def validate foo = Foo.find(id) raise "foo not found with id #{id}" unless foo location = Location.find_by(town:) raise "location not found with town #{town}" unless location raise "valuation does not match the minimum" unless valuation >= 10_000; end end # read the raw data rows = [[...], ...] # parse and validate the data records = rows.map do |row| record = Record.new(row) record.validate end # use the data records.each do |record| record.foo.location = record.location record.foo.valuation = record.valuation record.foo.save endThis parse, validate, and use approach is approporate for all cases where you are bringing data from the outside into your application, no matter what the outside source.
ps. These small, helper classes are your friends. Prefer them over your language's hash primitive as they provide great control. Most languages have efficient syntax for creating and using them.
Short list of useful Mac utilities
The Paris Olympics' pictograms
Remain in control of your runtime
Maintenance, evolution, and technical debt are different
I was listening to a recent Stack Overflow podcast on technical debit and was getting progressively annoyed. The cause of the annoyance was that the speaker was lumping all changes that were not feature changes as technical debit. The changes he was speaking about are better characterized as either maintenance, evolution, or technical debt.
Maintenance is bug fixes. The original implementation is deficient and needs to be repaired. This work is only related to the original design and current feature set.
Evolution is change needed due to alterations in the runtime environment. This work is only related to the original design and intended runtime environment. For example, you need to update a library and that results in a need to update the original implementation.
Technical debt comes about when the original design is unable to accommodate a new need. For whatever reason, it is no longer suitable.
Too often "technical debt" is used to mask an organization's lack of timely maintenance and planned evolution. Don't do that.
Naming things once
How is x = %i[a b c]
better than x = [:a, :b, :c]
? I find these kinds of shortcuts available in Ruby, and exploited to the maximum in Rails (et al), a severe detriment to understanding and exploring the code. Thankfully, JetBrain's RubyMine is almost always successful handling these pointless alternative codings. Ruby & Rails: Preventing successful grep-ing since forever.
Update: This is a somewhat related discussion on Hacker News, Greppability is an underrated code metric.
Understanding Patterns of Project Behavior
Adrenaline Junkies and Template Zombies: Understanding Patterns of Project Behavior is a useful catalog of project specimens and anti-specimen. As a catalog it does capture much of what I have experienced (and participated) during projects. Like Brook's The Mythical Man-Month, the information contained is, unfortunately for our industry, evergreen. I would not categorize this as a pattern or anti-pattern catalog, however, as, unlike good patterns, it does not regularly offer solutions. Nevertheless, its is worth having around if only to remind yourself that you are not crazy, the project is.