MacBook, defective by design banner

Put the knife down and take a green herb, dude.


One feller's views on the state of everyday computer science & its application (and now, OTHER STUFF) who isn't rich enough to shell out for

Using 89% of the same design the blog had in 2001.

Back-up your data and, when you bike, always wear white.

As an Amazon Associate, I earn from qualifying purchases. Affiliate links in green.


MarkUpDown is the best Markdown editor for professionals on Windows 10.

It includes two-pane live preview, in-app uploads to imgur for image hosting, and MultiMarkdown table support.

Features you won't find anywhere else include...

You've wasted more than $15 of your time looking for a great Markdown editor.

Stop looking. MarkUpDown is the app you're looking for.

Learn more or head over to the 'Store now!

Thursday, November 24, 2022

From Christian Heilmann (via Om Malik):

The global library is burning


On the social web, knowledge is smothered by agenda and on the publisher web by ads and paywalls and contracts. Ever tried to look up some news from 12 years ago? Back in library days you were able to do that. On news portals, most articles are deleted after a year, and on newspaper web sites you hardly ever get access to the archives – even with a subscription.

(Aside: Just checked my local newspaper, hoping for a counter-example, and it's true there too. Even with my monthly, inflated subscription payments, I don't have access to the archives. Though I bet my local library does, as it did (natch) during the "library days".)

This -- what ultimately boils down to the loss (or "unremediation") of [paper] ownership -- is, perhaps, why we need a more thorough

Labels: , , ,

posted by ruffin at 11/24/2022 09:49:00 AM
Wednesday, November 23, 2022

From MacRumors on Apple tracking you with first-party apps:

For example, according to the researchers, the App Store app continually harvested a wealth of usage data in real time, including user taps, apps searched for, viewed ads, and how long a user looks at any given app. Along with these details, Apple is also allegedly able to gather details typical of device fingerprinting methods, including ID numbers, device model, screen resolution, installed keyboard languages, and internet connection type.

In another example, the Mysk researchers said the Stocks app sent Apple a user's list of watched stocks, stocks viewed or searched for (including timestamps), as well as a record of news articles viewed in the app. This information was said to be sent to a web address via a transmission separate from the iCloud communication necessary to sync user data across devices.

I was of two distinct minds when I read this:

  1. Doesn't Apple need to know 80% of this stuff (what version OS, what phone, etc) when you're on the app store? And if you want to read an article, don't you need to request it? etc etc
  2. Boy, Apple really doesn't dogfood, do they?

Re: 1. -- here's a screencapture from a related YouTube video:

some of the info apple snoops while you're on the app store

That at first seems mostly like fair game info, doesn't it? But if you say "I don't want anyone tracking me," I can understand why you don't want and, what's more, wouldn't expect all of that pushed up into the pipe. As a developer, it'd be nice if Apple had to ask for that info the same as anyone else.

I wonder how much of Apple not truly dogfooding is so they can claim they can't split the software and hardware sides of the house. Because otherwise they really, really should dogfood as if they were any other app maker. Leveling the app-building playfield would improve every user's experience, because Apple could no longer take shortcuts when determining iOS' priorities.

"Oh, we can just grab that data from the OS," would no longer be a strategy, and, "Hey, we lose 90% of our conversion with this modal asking for full hardware info," would be enough for iOS to make those decisions move more smoothly, however that might be.

Oh, in other news, I finally got a Framework laptop. They had the 11th gen i5 refurb come back in stock for $600, and that's about what I'd pay to play around in this world. If there's a 13th gen CPU update next year that I can use, I might "really" shell out then, depending on how quickly and completely I take to Ubuntu. So far, versus my last foray into Linux on the desktop (admittedly over 10 years ago, I believe), it's nice and fast.

Labels: , , , ,

posted by ruffin at 11/23/2022 08:33:00 AM
Sunday, November 06, 2022

The joys of building your own computer

I've been interested in the Framework laptop for a while. I used to build towers, and my favorite part was always reusing parts. The lifecycle was always similar, and, like a Mobius strip, after you built the first one, you could enter the cycle at most any step.

  1. Learn new socket types.
  2. Buy a motherboard and processor.
  3. Reuse your old tower and, hopefully, PSU
  4. Reuse your old hard drive (and optical drive!)
  5. See if you can reuse RAM from last time.
  6. See if integrated graphics are better than recycling your old video card.

If you look closely, you could often, ignoring gaming for now, have a "new" tower for the price of a motherboard and processor because the parts were interchangeable. Increasingly, you'd get a boost for gaming too -- integrated graphics seemed to outpace reasonably priced previous-gen GPUs the last few I bought.

And you could push other upgrades until you felt like paying for them. You could, indpendently of the rest of the box...

  1. Upgrade your video card.
  2. Add RAM.
  3. Upgrade your PSU.
  4. Buy a new video card.

And those investments usually meant your next "mobo & proc" refresh would be that much better.

I do as much of this as I can with laptops. I've upgraded optical drives, RAM, SSDs, even an output port for my PowerBook 150.

But things are getting progressively worse. I was able to upgrade the SSD on my last MacBook Air, but I can upgrade nothing on my M1 now. My latest Thinkpad is an E series in part because the top-of-the-line T had one stick of soldered RAM.

Framework laptop: The price of entry is too darned high

Then I ran into the Framework laptop, which is, minus that the CPU is soldered permanently to the motherboard and there's no dedicated GPU, nearly as modular as those towers I built.

It's cool, but the price of entry is insane, unfortunately. The current price of entry is $1049 with an i5-1240P.

You can DIY for $819... sorta. The DIY model...

  • Doesn't have Windows ($139)
  • Doesn't have memory ($40 to match prebuilt)
  • Doesn't have an SSD ($59 for closest match to prebuilt)
  • Doesn't have a power adapter (doesn't seem to come with prebuilt either?)
  • No "expansion cards" ($36 to match prebuild)

That means to get the same as the prebuilt you've spending $1093. That's $44 more for the privilege of building it yourself.

Worse, there are no creative ways to get in cheaper.

  • There's a refurbished previous gen for $599 listed, but it hasn't been in stock since I've been keeping track.
  • There's a refurb prev gen with i7-1165G7 for $799 -- but for $20 you get a much better box: MUCH faster processor, stronger backplate, and all new components. That's a deal you gotta take.
  • You can put together the parts separately from the marketplace for a "true DIY", but ordering the parts yourself still runs $882 even with a prev gen i5. Again, horrible price vs. new DIY.
    • Insult to injury, you can't get the trackpad or battery this way yet.


I did check for people selling old mainboards, as the previous gen i5 is still an overpriced $350 on Framework's site, but didn't turn up anything. Makes me wonder how many folks have purchased and how many of those have upgraded. I would've expected some supply of used mainboards. If you could bag one for $200, maybe we're making some progress, but there's nothing there or on eBay.

Long-term isn't much better

But now compare your next upgrade. Can we board the tower's Mobius strip of infinite upgrades?

Unfortunately the cheapest 12th gen Intel chip mainboard from Framework is $449. Looks like we've got an issue.

Compare that price to new gaming laptop deals, which aren't perfect dev laptops, but aren't bad either. Here are two deals currently live from SlickDeals:

And both of those have real graphics options. That's bad in that it usually means, as they're gaming laptops, you can't charge via USB-C, the batteries aren't great, maybe the webcam is crap, and the keyboards aren't top notch.

But each of those are hundreds less than a Framework without memory or an SSD. That is, if we pretend that our second round of owning a Framework only runs $450, we're just barely breaking even with buying two gaming laptops. We have to go two rounds of upgrades just to argue we've come out ahead.

That doesn't sound horrible, but at least the extra gaming laptops would give us (potentially) two more RAM sticks and SSDs to reuse down the line -- and/or a used computer we could resell.

Framework is great, reduces ewaste, and would be cheaper to repair if something died, but the Framework simply is not a cost-effective option for your latest-gen laptop, even looking long-term.

Ugh. I want to encourage what they're doing, and feel like I oughta put my money where my mouth is, but, to date, I just can't.

Ways that I'm trying to talk myself into getting one anyway:

  1. Better webcam
  2. Better keyboard (right?)
  3. Better Linux support
  4. Easily replaceable battery once it dies

That's all I got. So far, still not enough. The Framework laptop tax is too high.

Labels: , , ,

posted by ruffin at 11/06/2022 01:23:00 PM
Friday, November 04, 2022

I'm going to tell a story I really liked telling when I taught Public Speaking or Business Writing, though I probably snuck it eventually in any time I taught.

It's about knowing your audience and identifying when you're an expert speaking to others largely because they're not. It's about what my business writing textbook's author called "You-Attitude". Someone who taught from that book at Radford said the following are a few "you-attitude principles":

  • Look at situations from the reader’s perspective
  • Emphasize what the reader wants to know
  • Respect the reader’s intelligence
  • Protect the reader’s ego

You can learn to do this, but it comes much more naturally to some than it does to others.

Anyhow, it's story time.

One day, for lunch, I walked into a Hardee's, a hamburger joint, and had the mis/fortune to overhear this conversation going on in the line in front of me.

  • Hardee's employee: What would you like?
  • Customer: A cheeseburger with lettuce, tomato, ketchup, and mayo.
  • Employee, after looking down at the register: But what don't you want on it?
  • Customer: I don't know. Everything that's not lettuce, tomato, ketchup, and mayo?
  • Employee: I understand what you want. But what don't you want?

Ad infinitum. (It really went on like this for a while.)


I mean, obviously the employee's register must have had some way to take toppings away from an order, but not a way to express the opposite: Exclusive toppings to put on.

Look, ultimately, this is a common thing at restaurants, and if you've been a few times & paid attention, you might, as a customer, be able to handle just such a conversation. Maybe you've ordered at Jersey Mike's, which has some toppings preselected for your sandwich so you can get it "Mike's Way".

If you're ordering in person, you might say, "I want it Mike's Way, minus onions, vinegar, & oil, plus mayo."

But if you have no idea what Mike's Way is, are you stuck? No! Even though it's usually posted on the ordering board in front of you, most sandwich makers (are they called "artists" now? Oops, that's Subway) can still figure out how to do "lettuce, tomato, oregano, salt, and mayo" if that's what you order.

And that's the problem we had here. The Hardee's employee couldn't translate the customer's order to what the register needed to accept the order. No matter how long we stood there asking, "Right, but what dontcha want?" the customer would never know what compromised the default "Hardee's Way". And it appeared the employee either didn't know or couldn't articulate that either.

That's the epitome of "Me-Attitude": The inability to frame a situation from a point of view other than your own. You need to be able to reframe the discussion around how it affects your audience -- the "you". When in doubt, assume they don't know what you know until they tell you otherwise. And when they've removed all doubt that they don't know something you assumed they did, back up and catch them up.

I run into this inability to describe something from another's point of view at the office with some frequency. Once, we had someone put up code to review in an area where I'd never worked before, changing code I'd never seen, fixing a bug I'd never read.

  • Me: Okay, catch me up. Describe what you're doing. What problem were you solving? What other ideas did you consider and not use? How did you fix it and why?
  • Them: It's all in the bug repot [plot twist: it was not]. We couldn't use jQuery because we're catching bubble events at the document level.
  • Me: Great, but why are we listening to mousedown events at the document level? What problem did that solve?
  • Them: That fixes the bug.
  • Me: Cool, cool.
    • Also me: This is probably going to take me a while to review.

Look, I kind of get it. "Them" was saying, "If you had my context, you'd understand what's happening." But I didn't have the context.

Them's answers were wholly unsatisfying in the "guess the Hardee's Way" way. I'm ultimately asking, "Can you give me the context so that I can intelligently review this without doing a forensic study of the code first?" The answer in this case was, unfortunately, no.

I didn't have any more luck convincing "Them" to explain what came on the burger by default than the person in front of me did.

What the audience needed to know wasn't emphasized. These are all "Me-Attitude" conversations, not "You-Attitude" ones. And that makes for clunky communication and inefficient work.

Moral of the story: Be able to tell your audience what's on your burger.

Labels: , , , ,

posted by ruffin at 11/04/2022 01:30:00 PM
Tuesday, November 01, 2022

I always forget where this is, and had another iPhone die from a fall yesterday, which often requires the SIM card moving to my backup Android until a replacement iPhone is secured.
That site where you get sent an SMS on the new phone and Apple removes your phone number from Message's SMS black hole is this:
Remember when this didn't exist? :shudder:

Labels: ,

posted by Jalindrine at 11/01/2022 10:45:00 AM
Monday, October 31, 2022

Amazingly (or perhaps not), Firefox (macOS 106.0.1 (64-bit)) slammed me back to using Google as its  toolbar search engine. I had been using DuckDuckGo for years.

Luckily Firefox had a pop-up telling me as much, actually bragging about it, so I knew to set it back. But I did test it once before swapping, and this was not a drill. The update to Firefox wasn't showing me the wrong dialog; it had slammed me back to Google.


Profiteering weirds the internet.

Labels: , ,

posted by ruffin at 10/31/2022 09:09:00 AM
Friday, October 28, 2022

VIm remains one of my most useful tools.


From a somewhat useful SO comment:

Fwiw -- Looks like g [in a command like :g/pattern/d] is for "global" and v [like we have here] is for "inverse" (if you believe what you read on vim.famdom).

Labels: , ,

posted by ruffin at 10/28/2022 05:05:00 PM
Thursday, October 27, 2022

A note to self I might have already noted... myself.

    console.log(value[1] + ": " + value[2][0]);


So look, I'm at a company where much of the app is still in AngularJS. The downside is that this reminds me of a post I'd shared before that said:

If your list of things to develop is really a list of things that you won’t have to do in a more functional environment, none of which will make you more employable elsewhere… it’s time to walk away.

That's not wrong. More experience in AngularJS isn't making me more employable at this point unless I want to work in the small subset of positions that plan to continue using it well past its dead-on date. Which, again, was January of this year.

That admission aside, as I've mentioned before, there is, however, a practical advantage: Most of the questions I have when I'm developing have nearly canonical answers. I mean, have you seen StackOverflow's blog on the Next.JS conference? The blog's title is "Goodbye Webpack, hello Turbopack!". With just a little bit of exaggeration, webpack, we barely knew ye.

I was looking to get us to a React stack, and I'd initially liked this "minimal" build tool suite over at 2ality. Okay, okay, it's been over three years, but it's now kaput. Gone. Snowpack is dead. I'd heard a bit about ViteJS already a bit from my RSS sub to Shawn Wildermuth's blog, and sure enough that's what Snowpack says to use now. If I'd learned Snowpack inside and out (not that it was tough to learn, and I've been playing with it on and off for a few years), that'd be nearly wasted time now.

Look, it's fine to need to keep up a little, it's not like .NET 6 doesn't have a decent amount of new stuff (does it though?), and maybe it's just that I've been around long enough to see things change several times, but it doesn't really have to, does it? There are database admins who haven't needed to learn anything exceptional for their whole career. I mean, look, it's not like our buddy Mr. Dave hasn't kept things moving, but it ain't client-side JavaScript. He's had a chance to get good at what he does, to really drill-down and understand rdbms engines.

Okay, yes. I'm a little jealous. 😏

And if I haven't mentioned it before, I'm pretty sure it's because of conferences, YouTube, and Twitter. Everybody who wants to be a bleeding edge type can be, and they all race each other to be the new expert of something nobody else can be yet because it was just released. God made humans, but constantly evolving tech stacks made humans equal[ly clueless].

That said, I do need to write my follow-up to my intro to why we're using transpilation-free Preact. Preact really is a wonderful bridge from a hopelessly out of date client-side app to, well, at least a TypeScript-powered React refactor. Need to develop something new now but can't, for whatever reason, create all new tooling?

If you can limit support to IE 9+ or so, Preact (with hooks) is your stack. We've used Preact with Enzyme to create a tested, cradle-to-grave page with a reasonably complex UI and have it running well right smack in the middle of an AngularJS app. Set down new work in Preact now, start eating from the top with React, and when they meet in the middle, it's a very quick port.

So maybe I won't need to remember that cheat to get all directives, constants, etc out of an AngularJS site for too long. But when I forget again, at least I'll know I've got it sitting here.

Labels: , , , , ,

posted by ruffin at 10/27/2022 04:44:00 PM
Thursday, September 01, 2022

So we can edit tweets. Or can soon. 

One suggestion:

Replies appear under the edit to which they’re applied. 

That means your previous edits, if they have a reply, will appear unedited in others’ timelines. But if someone clicks that tweet, some UI that shows it and its current edit would appear. 

Would that drop quick, pre-edit replies from the OP’s thread? Potentially. Now you could dump replies with an edit. 

Maybe you keep the replies but group by (and clearly indicate that they were added to) a previous version of the tweet. 

It’s a complicated problem, but there are deliberate and maintainable solutions.


posted by Jalindrine at 9/01/2022 10:35:00 AM
Thursday, August 04, 2022

I've run into there being two different sets of collections in .NET before, generic vs. well, vs. not (?), but never really sat down to understand it.

They are very different. From

ICollection<T> and ICollection are actually very different interfaces that unfortunately share a name and not much else.


From an MSDN link in that answer:

Collection<T> seems like ICollection, but it’s actually a very different abstraction. We found that ICollection was not very useful. At the same time, we did not have an abstraction that represented an read/write non-indexed collection. ICollection<T> is such abstraction and you could say that ICollection does not have an exact corresponding peer in the generic world; IEnumerable<T> is the closest.

Here they are:

This means LINQ works on the latter but not the former. Luckily ICollection (no T) is still enumerable, so foreach away! (Are there considerations about iteration, like can you do it twice? Maybe? I haven't run into one yet.)

There are similar issues with many collection types, reviewed in some detail on that page, including IDictionary...


IDictionary<TKey, TValue> is roughly equivalent to IDictionary.

Which includes a quick follow-up on DictionaryBase:


We don't recommend that you use the DictionaryBase class for new development. Instead, we recommend that you use the generic Dictionary<TKey,TValue> or KeyedCollection<TKey,TItem> class . For more information, see Non-generic collections shouldn't be used on GitHub.

That said, DictionaryBase's entry gives us a nice example implementation so we don't have to dig up a concrete implementation somewhere else, like Exception.Data (which is what created this rabbit hole for me).

Using that example ShortstringDictionary we can illustrate our issue this way:

ShortstringDictionary oldStyleDictionary = new ShortstringDictionary();

oldStyleDictionary.Add("One", "a");
oldStyleDictionary.Add("Two", "ab");
oldStyleDictionary.Add("Three", "abc");
oldStyleDictionary.Add("Four", "abcd");
oldStyleDictionary.Add("Five", "abcde");

//var filtered = oldStyleDictionary.Where( // <<<<< doesn't exist; no LINQ

So notice that there's no LINQ here, so we can't Where our ShortstringDictionary.

But the dictionary IS enumerable. Well, kinda. The Keys and Values collections are.

So we can foreach through Keys and, here, insert each key/value into a System.Collections.Generic.Dictionary<TKey,TValue> (here, specifically, a Dictionary<string, string>).

Note that the Keys collections' values, though the implementation forces them to be strings, are NOT typed!

Keys is a System.Collections.ICollection (from the humourously named System.Collections.NonGeneric.dll).

Dictionary<string, string> newStyleDictionary = new Dictionary<string, string>();
foreach (object key in oldStyleDictionary.Keys)
    string keyAsString = key.ToString();
    Console.WriteLine($"{key}: {oldStyleDictionary[keyAsString]}");

    newStyleDictionary.Add(keyAsString, oldStyleDictionary[keyAsString]);


Now our generic Dictionary<TKey,TValue> can use LINQ's Where to filter.

var filtered = newStyleDictionary.Where(x => x.Value.Contains("c"));

foreach (var kvp in filtered)
    Console.WriteLine($"{kvp.Key}: {kvp.Value}");

Here endeth the lesson.

And begineth another -- quick update from this SO answer:

The most painful difference is that for the generic Dictionary<string, string>, when I call this[key] for a key that does not exist, I get an exception stating the key does not exist.

On a DictionaryBase I get back null with no exception. This was painful in my case because the system was full of code that did not check that the dictionary ContainsKey before trying to get the keys value. It was made more painful by me assuming I messed something up with serialization.

That appears to be true!

Console.WriteLine($"{bogusKey}: {oldStyleDictionary[bogusKey]}"); // writes "bogus: "
Console.WriteLine($"{bogusKey}: {newStyleDictionary[bogusKey]}"); // throws System.Collections.Generic.KeyNotFoundException 

Labels: , ,

posted by ruffin at 8/04/2022 02:34:00 PM

Support freedom
All posts can be accessed here:

Just the last year o' posts:

URLs I want to remember:
* Atari 2600 programming on your Mac
* joel on software (tip pt)
* Professional links: resume, github, paltry StackOverflow * Regular Expression Introduction (copy)
* The hex editor whose name I forget
* JSONLint to pretty-ify JSON
* Using CommonDialog in VB 6 * Free zip utils
* git repo mapped drive setup * Regex Tester
* Read the bits about the zone * Find column in sql server db by name
* Giant ASCII Textifier in Stick Figures (in Ivrit) * Quick intro to Javascript
* Don't [over-]sweat "micro-optimization" * Parsing str's in VB6
* .ToString("yyyy-MM-dd HH:mm:ss.fff", CultureInfo.InvariantCulture); (src) * Break on a Lenovo T430: Fn+Alt+B
email if ya gotta, RSS if ya wanna RSS, (?_?), ¢, & ? if you're keypadless

Powered by Blogger Curmudgeon Gamer badge
The postings on this site are [usually] my own and do not necessarily reflect the views of any employer, past or present, or other entity.