
Which photos would you remove?
make dev
on current master, I get a proper version. Same with make server
. Assuming you cloned the repo, do you have any (uncommited) changes? What does git status
tell you?
Also, you would need to host not your own hash files, but everybody else's as well you follow. Otherwise, what is that supposed to achieve? If people are already following my feed, they know what hashes I have, so this is to no use of them (unless they want to look up a message from an archive feed and don't process them). But the far more common scenario is that an unknown hash originates from a feed that they have not subscribed to.
Additionally, yarnd's URL schema would then also break, because
https://twtxt.net/twt/<hash>
now becomes https://twtxt.net/user/prologic/<hash>
, https://twtxt.net/user/bender/<hash>
and so on. To me, that looks like you would only get hashes if they belonged to this particular user. Of course, you could define rules that if there is a /user/
part in the path, then use a different URL, but this complicates things even more.Sorry, I don't like that idea.
I had some sore muscles after yesterday's waste paper collection with the scouts. So, I only went for a short trip to my closest backyard mountain. Watching two rock climbers was interesting. That's not something I see very often.

https://lyse.isobeef.org/waldspaziergang-2025-03-09/
* storage/ defines the interface
* sqlite/ implements the storage interface
* mock/ extends the SQLite implementation by some mocking capabilities and assertions
Here, however, there are no storage subpackages. It's just
storage
, that's it. Everything is in there. The only implementation so far is an SQLite backend that resides in storage
. My RAM storage is exactly that SQLite storage, but with :memory:
instead a backing file on disk. I do not have a mock storage (yet).I have to think about it a bit more, but I probably have to do exactly that in my
tt
rewrite, too. Sigh. I just have the feeling that in storage/sqlite/sqlite_test.go I cannot import storage/mock for the helper because storage/mock/mock.go imports and embeds the type from storage/sqlite. But I'm too tired right now to think clearly.*
* storage/
* storage.go: defines a
Storage
interface* sqlite.go: implements the
Storage
interface* sqlite_test.go: originally had a function to set up a test storage to test the SQLite storage implementation itself:
newRAMStorage(testing.T, $initialData) *Storage
* controller/
* feeds.go: uses a
Storage
* feeds_test.go: here I wanted to reuse the
newRAMStorage(…)
functionI then tried to relocate the
newRAMStorage(…)
into a* teststorage/
* storage.go: moved here as
NewRAMStorage(…)
so that I could just reuse it from both
* storage/
* sqlite_test.go: uses
testutils.NewRAMStorage(…)
* controller/
* feeds_test.go: uses
testutils.NewRamStorage(…)
But that results into an import cycle, because the
teststorage
package imports storage
for storage.Storage
and the storage
package imports testutils
for testutils.NewRAMStorage(…)
in its test. I'm just screwed. For now, I duplicated it as newRAMStorage(…)
in controller/feeds_test.go.I could put
NewRAMStorage(…)
in storage/testutils.go, which could be guarded with //go:build testutils
. With go test -tags testutils …
, in storage/sqlite_test.go could just use NewRAMStorage(…)
directly and similarly in controller/feeds_test.go I could call storage.NewRamStorage(…)
. But I don't know if I would consider this really elegant.The more I think about it, the more appealing it sounds. Because I could then also use other test-related stuff across packages without introducing other dedicated test packages. Build some assertions, converters, types etc. directly into the same package, maybe even make them methods of types.
If I went that route, I might do the opposite with the build tag and make it something like
!prod
instead of testing. Only when building the final binary, I would have to specify the tag to exclude all the non-prod stuff. Hmmm.*
* storage/
* storage.go: defines a
Storage
interface* sqlite.go: implements the
Storage
interface* sqlite_test.go: originally had a function to set up a test storage to test the SQLite storage implementation itself:
newRAMStorage(testing.T, $initialData) *Storage
* controller/
* feeds.go: uses a
Storage
* feeds_test.go: here I wanted to reuse the
newRAMStorage(…)
functionI then tried to relocate the
newRAMStorage(…)
into a* teststorage/
* storage.go: moved here as
NewRAMStorage(…)
so that I could just reuse it from both
* storage/
* sqlite_test.go: uses
testutils.NewRAMStorage(…)
* controller/
* feeds_test.go: uses
testutils.NewRamStorage(…)
But that results into an import cycle, because the
teststorage
package imports storage
for storage.Storage
and the storage
package imports testutils
for testutils.NewRAMStorage(…)
in its test. I'm just screwed. For now, I duplicated it as newRAMStorage(…)
in controller/feeds_test.go.I could put
NewRAMStorage(…)
in storage/testutils.go, which could be guarded with //go:build testutils
. With go test -tags testutils …
, in storage/sqlite_test.go could just use NewRAMStorage(…)
directly and similarly in controller/feeds_test.go I could call storage.NewRamStorage(…)
. But I don't know if I would consider this really elegant.The more I think about it, the more appealing it sounds. Because I could then also use other test-related stuff across packages without introducing other dedicated test packages. Build some assertions, converters, types etc. directly into the same package, maybe even make them methods of types.
If I went that route, I might do the opposite with the build tag and make it something like
!prod
*


86 more photos: https://lyse.isobeef.org/wanderung-auf-den-hohenrechberg-2025-03-03/
Forest animals also have to do the laundry, they even have a proper clotheshorse! See: https://lyse.isobeef.org/wanderung-zu-den-schurrenhoffuechsen-2021-05-15/07.jpg :-D
Having said that, it appears that this only affects me personally, noone else. I don't know of any other client that saves read statuses. But don't worry about me, all good. Just keep doing what you've done so far. I wanted to mention that only for the sake of completeness. :-)
In all reality, though, I don't see that our community will come to an agreement. Some folks just don't want to give up on the content-based addressing scheme.
There were even patches of snow left up top, that was unexpected. Also, somebody created a cool rock art piece on a tree stump. That one rock absolutely looked like a face. Crazy!

Enjoy: https://lyse.isobeef.org/waldspaziergang-2025-03-01/
I'm also working on my rewrite at the moment, but that started… *looking at the git history*… oh wow! O_o Over two years ago! I just implemented jumping to the next/previous unread message.
As for the potentially reduced code coverage with a non-TDD approach, I can easily see which parts are lacking tests and hand them in later. So, that's largely a specious argument. Granted, I can forget to check the coverage or simply ignore it.
I agree with John, TDD results in less elegant code or requires more refactoring to tidy it up. Sometimes, it's also not entirely clear at the beginning how the API should really look like. It doesn't happen often, but it does happen. Especially when experimenting or trying out different approaches. With TDD, I then also have to refactor the tests which is not only annoying, but also involves the danger of accidentally breaking them.
TDD only works really well, if you have super tiny functions. But we already established that I typically don't like tiny methods just for the purpose of them being extremely short.
When fixing a bug, I usually come up with a failing test case first to verify that my repaired code later actually resolves the problem. For new code, it depends, sometimes tests first, sometimes the productive code first. Starting off with the tests requires the API to be well defined beforehand.
I didn't come across John Ousterhout or any of his work before, at least not deliberately. So, this document is my first contact.
I only finished the chapter on comments and I totally agree with John so far. This document just manifests to me how weird Bob's view is on certain subjects.
I always disagreed with the concept of a maximum method length. Sure, generally, shorter functions are probably better, but it always depends. And I've certainly seen super short methods that just made the code flow even worse to follow. While "one function should only do one thing" is a nice general rule, I'm 100% in team John with the shown examples. There are cases, where this doesn't help readability at all. Not even close.
To me, a function always has to justify its existence. Either by reusing it at least at another place or by coming up with dedicated tests for it. But if it is just called once and there are no tests, I almost always decide against it. Personally, I don't mind longer methods. We just recently had a discussion about that and I lost against two other workmates who are more in Uncle Bob's camp, they refactored one medium sized method into three very short ones. Luckily, we agree on most other topics.
Lol, what!? The shorter the method, the longer the variables inside? I first thought I misread or the writeup mixed it up. I'll always do it the other way around.
I've been also bitten badly by outdated comments in the past, but Bob must have worked on really terrible projects to end up with such an attitude to dislike comments. Oh well. No doubt, I've come across by several orders of magnitude more useless comments, in my experience (autogenerated) JavaDocs fall in the category more frequently than not. So, I know that there are different types of comments. A comment doesn't automatically mean that it is good and justified.
But I also partially agree with Bob and John and think that a good name has a proper chance to save a comment. Though, when in doubt, I go John's route and use a shorter name with a comment rather than use a kilometer long identifier. Writing good comments typically takes some time, sometimes much longer than writing the code. It regularly takes me several minutes. It's a hard art.
I perhaps should read up on John's work. He seems to be more reasonable and likeminded. :-) Let me continue to complete this document.
I have configured my vim to use a tab width of four. So, I noticed that especially https://www.falsifian.org/blog/2021/06/04/catalytic/reachability_with_stack.cc (but also partially the other C++ file) mixes tabs and spaces for indentation. :-)
Even though I'm the last one who wouldn't be glad about banning the nazis, I'm not a fan of banning parties in general. I believe that a healthy democracy has to withstand extremists. Whether it's still healthy is debatable. To me it appeared that the failed attempts to ban NPD in the past actually helped them gain more supporters.
The big established parties are all bad traitors. I blame them and their actions to help raise AfD. They just give a fuck about the ordinary people, they're only concerned about their private gain and power. I bet nothing will change, to the contrary, it will only get worse. The winners do have the chance to turn it for the better, but they just will not. No way, unfortunately.
But then, we must not forget that people are just dumb and stupid, too. Also, that won't change. AfD won't help these idiots either, but they still vote for them. I also don't understand how there is still so much support for the other big parties left. Education is important. Very important. But I have the impression that we're lacking it.
There's a bad looking crack in the climbing rock in 10. When you have eagle eyes, you might be able to see the hooks in the cliff for the climbing ropes. I haven't seen this one before. Also, it looked like several cubic meters of earth, grass and rock fell off the top.
On the way home, it got much more sunny. I found yet another skyrocket stick. That was pretty neat. And we saw the first field of snowdrops. With some bees checking them out. In total we walked a bit over 15km.

More pics: https://lyse.isobeef.org/waldspaziergang-2025-02-23/
convert -strip -quality 70 -resize 300x original.jpg resized.jpg
"original.jpg" being the filename of the input file and "resized.jpg" the filename of the output. You can play around with the width, "300x" means 300 pixels wide and the height is determined automatically to still remain in the same ratio. The quality is how much to compress it. The closer to 0 the value gets, the worse the result, but also smaller in file size. More towards 100 and the quality improves together with a larger file size.
You have to install the package "imagemagick" for this to work, I believe.
I like how Ian's and your photo complement each other, winter and summer join forces for something special. :-)
@thecanine I agree!
But maybe that also means I'm one of these "told you so" guys. Not sure.
Yeah, sounds like another hype train arriving at the station.
tt
rewrite in Go and quickly implemented a stack widget for tview. The builtin Pages is similar but way too complicated for my use case. I would have to specify a mandatory name and some additional options for each page. Also, it allows me to randomly jump around between pages using names, but only gives me direct access the first, however, not the last page. Weird. I don't wanna remember names. All I really need is a classic stack. You open a new fullscreen dialog and maybe another one on top of that. Closing the upper most brings you back to the previous one and so on.The very first dialog I added is viewing the raw message text. Unlike in @arne's TwtxtReader, I'm not able to include the original timestamp, though. I don't have it in its original form in the database. :-/
Next up is a URL view.
][
) you will find a bunch more of these. It goes without question he never typed that in his feed. My client saves each twt hash I've explicitly marked read. A few days ago, I got plenty of apparently years old, yet suddenly unread messages. Each and every single one of them containing this repeated bracketed text thing. The only conclusion is that something messed up the feed again.
][
) you will find a bunch more of these. It goes without question he never typed that in his feed. My client saves each twt hash I've explicitly marked read. A few days ago, I got plenty of apparently years old, yet suddenly unread messages. Each and every single one of them containing this repeated bracketed text thing. The only conclusion is that something messed up the feed again.

Wer hat sich zu dieser Meldung diese Knopfauswahl überlegt und dann auch noch die Icons dazu ausgedacht? Und warum hat's das Zertifikat überhaupt schon wieder zerlegt? Und wieso kommt der Dialog direkt wieder in ner Endlosschleife hoch, wenn ich abbreche? Komplettversagen nach Strich und Faden an allen Enden. Allen. Grrr, so viel Hass! Ich schalt besser die Büchse aus.
No, in all seriousness, that's a tough one. Try to figure out the requirements and write tests to cover them. In my experience, if there is no good documention, tests might also be lacking. It goes without saying that you have to understand the code segments first before you can begin to refactor them. Commit even earlier and more often than usual, this will help you bisecting potentially introduced bugs later on. Basically baby steps.
But it also depends on the amount of refactoring required. Maybe just scrap it entirely and start from scratch. This might not be feasible due to e.g. the overall project size, though.
A newsreader without read flags would be totally useless to me. But I also do not subscribe to fire hose feeds, so maybe that's a different story with these. I don't know.
To me, filtering read messages out and only showing new messages is the obvious solution. No need for notifications in my opinion.
There are different approaches with read flags. Personally, I like to explicitly mark messages read or unread. This way, I can think about something and easily come back later to reply. Of course, marking messages read could also happen automatically. All decent mail clients I've used in my life offered even more advanced features, like delayed automatic marking.
All I can say is that I'm super happy with that for years. It works absolutely great for me. The only downside is that I see heaps of new, despite years old messages when a bug causes a feed to be incorrectly updated (https://twtxt.net/twt/tnsuifa). ;-)

Just search for
][
in https://twtxt.net/user/mckinley/twtxt.txt and you'll see.