@eaplmx@twtxt.net i used internet when it cost 13c per minute. my parents would get so mad if i went over the monthly base minutes CompuServe gave us.
@prologic@twtxt.net duud use an ad block on youtube.
ahh this is useful https://go.dev/doc/modules/managing-dependencies. the go culture doesnāt typically have large dependency graphs like Ruby or JS.
@lyse@lyse.isobeef.org @prologic@twtxt.net yeah that was how i did it too. I think ill start using the debug version in new stuff since its been added. My comment was around assigning the result of an anonymous function to a a variable.
Tell me you write go like javascript without telling me you write go like javascript:
import "runtime/debug"
var Commit = func() string {
if info, ok := debug.ReadBuildInfo(); ok {
for _, setting := range info.Settings {
if setting.Key == "vcs.revision" {
return setting.Value
}
}
}
return ""
}()
Bad Date
ā Read more
Y2K and 2038
ā Read more
@mckinley@twtxt.net Thank you! I didnāt even know about signing and encrypting XML documents. Right, RSS is a little bit messy.
Unfortunately, the autodiscovery document in one of your linked resources does not exist anymore. What annoys me in Atom is the distinction between <id>
and <link>
. I always want my URL also to be my ID, so I have to duplicate that ā unnecessarily in my opinion.
Also, never found a good explanation why I should add <link rel="self" ⦠/>
to my feeds. I just do, but I donāt understand why. The W3C Feed Validation Service says:
[ā¦] This value is important in a number of subscription scenarios where often times the feed aggregator only has access to the content of the feed and not the location from which the feed was fetched.
This just sounds like a very questionable bandaid to bad software architecture. Why would the feed parser need access to the feed URL at this stage? And if so, why not just pass down the input source? Just doesnāt make sense to me.
Also, I just noticed that I reference the http://purl.org/rss/1.0/modules/syndication/
namespace, but donāt use it in most of my feeds. Gotta fix that. Must have copied that from my yfav feed without paying attention what Iām doing.
Your article made me reread the Atom spec and I found out, that I can omit the <author>
in the <entry>
when I specify a global <author>
at <feed>
level. Awesome! Will do that as well and thus reduce the feed size.
Wirecutter Recommendation
ā Read more
it uses the queries you define for add/del/set/keys. which corrispond to something like INSERT INTO <table> (key, value) VALUES ($key, $value)
, DELETE ...
, or UPDATE ...
the commands are issued by using the maddycli but not the running maddy daemon.
see https://maddy.email/reference/table/sql_query/
the best way to locate in source is anything that implements the MutableTable interface⦠https://github.com/foxcpp/maddy/blob/master/framework/module/table.go#L38
I was inclined to let this go so as not to stir anything up, but after some additional thought Iāve decided to call it out. This twt:
is exactly the kind of ad hominem garbage I came to expect from Twitterā¢, and Iām disappointed to see it replicated here. Rummaging through someoneās background trying to find a āgotchaā argument to take credibility away from what a person is saying, instead of engaging the ideas directly, is what trolls and bad faith actors do. Thatās what the twt above does (falsely, I might addāwhatās being claimed is untrue).
If you take issue with something Iāve said, you can mute me, unfollow me, ignore me, use TamperMonkey to turn all my twts into gibberish, engage the ideas directly, etc etc etc. There are plenty of options to make what I said go away. Reading through my links, reading about my organizationās CEOās background, and trying to use that against me somehow (after misinterpreting it no less)? Besides being unacceptable in a rational discussion, and besides being completely ineffective in stopping me from expressing whatever it is you didnāt like, itās creepy. Donāt do that.
Very cool. I am digging the multipurpose use for this kind of parser.
Division Notation
ā Read more
@movq@www.uninformativ.de, well, yes, that has always been the case. Not just on jenny, but on Yarn. I canāt follow everything, and everyone. To see whether is a reply, a simple h
on mutt shows the headers, and there you have it. That is not too convoluted, is it? I mean, if you really want to knowābut why?āit is a simple key press. If I donāt see a context on something as obvious as the example you used, it is simply a reply to someone I donāt follow, and pretty much ignore. End of story. š
@movq@www.uninformativ.de, any plans still to clean up the hash from the twtxtās body? Maybe a Festivus gift? You know, āfor the rest of usā. :-D
š£ NEW: Announcing the new and improved Yarns search engine and crawler! search.twtxt.net ā Example search for āHello Worldā Enjoy! š¤ ā @darch@neotxt.dk When you have this, this is what we need to work on in terms of improving the UI/UX. As a first step you should probably try to apply the same SimpleCSS to this codebase and go from there. ā In the end (didnāt happen yet, time/effort) most of the code here in yarns
will get reused directly into yarnd
, except that Iāll use the bluge indexer instead.
@abucci@anthony.buc.ci Do you happen to use Signal btw? š¤ If you do, it would be great if you could join the Yarn.social Signal Group (_at least until we have Group support and better apps for Salty.im š )
I think Email has been broken on my Pod since some time now since @lyse@lyse.isobeef.org made this commit the default behaviour of the flags/env had changed requiring SMTP_PORT
to be set (used to default toi 25
) ooops š
@xuu@txt.sour.is proof of stake = people with money get more money. It accelerates the wealth inequality problems that are already plaguing us. Crypto has even worse wealth inequality than fiat currency systems, which is 100% predictable.
-1 for the negative on environment all that electricity uses. Still waiting on proof of stake.
It is also too overrun with Tech Bros scamming people to get rich quick.
It was a fun ride back when I first bought in. But I have since cached out for my lambos and such.
pondering about the difference between my use of the tags #idea and #halfbaked . halfbaked looks like something in a more advanced stage, merely lacking execution #meta
@abucci@anthony.buc.ci Its not better than a Cat5e. I have had two versions of the device. The old ones were only 200Mbps i didnāt have the MAC issue but its like using an old 10baseT. The newer model can support 1Gbps on each port for a total bandwidth of 2Gbps.. i typically would see 400-500Mbps from my Wifi6 router. I am not sure if it was some type of internal timeout or being confused by switching between different wifi access points and seeing the mac on different sides.
Right now I have my wifi connected directly with a cat6e this gets me just under my providers 1.3G downlink. the only thing faster is plugging in directly.
MoCA is a good option, they have 2.5G models in the same price range as the 1G Powerline models BUT, only if you have the coax in wall already.. which puts you in the same spot if you donāt. You are for sure going to have an outlet in every room of the house by code.
I maintain keys for my email addresses.. but like most in this thread i almost never receive encrypted emails.. other than the BTC exchange i use that sends automated mail encrypted.
I use IP over Alternating Current at home. The only issue is the bridges forget device MACs sometimes and I get weird states where I can ping a deviceA and the the internet from deviceB. But deviceA can only ping device B even though they are on the same subnet and default gateway.
Does anyone of you use PGP encrypted mail, or any kind or email encryption? Why? Why not?
@prologic@twtxt.net I think we could use deltachats new decentralising app format for it: https://delta.chat/en/2022-06-14-webxdcintro
š Hello @burglar@txt.sour.is, welcome to txt.sour.is, a Yarn.social Pod! To get started you may want to check out the podās Discover feed to find users to follow and interact with. To follow new users, use the ⨠Follow
button on their profile page or use the Follow form and enter a Twtxt URL. You may also find other feeds of interest via Feeds. Welcome! š¤
(cont.)
Just to give some context on some of the components around the code structure.. I wrote this up around an earlier version of aggregate code. This generic bit simplifies things by removing the need of the Crud functions for each aggregate.
Domain ObjectsA domain object can be used as an aggregate by adding the event.AggregateRoot
struct and finish implementing event.Aggregate. The AggregateRoot implements logic for adding events after they are either Raised by a command or Appended by the eventstore Load or service ApplyFn methods. It also tracks the uncommitted events that are saved using the eventstore Save method.
type User struct {
Identity string ```json:"identity"`
CreatedAt time.Time
event.AggregateRoot
}
// StreamID for the aggregate when stored or loaded from ES.
func (a *User) StreamID() string {
return "user-" + a.Identity
}
// ApplyEvent to the aggregate state.
func (a *User) ApplyEvent(lis ...event.Event) {
for _, e := range lis {
switch e := e.(type) {
case *UserCreated:
a.Identity = e.Identity
a.CreatedAt = e.EventMeta().CreatedDate
/* ... */
}
}
}
Events
Events are applied to the aggregate. They are defined by adding the event.Meta
and implementing the getter/setters for event.Event
type UserCreated struct {
eventMeta event.Meta
Identity string
}
func (c *UserCreated) EventMeta() (m event.Meta) {
if c != nil {
m = c.eventMeta
}
return m
}
func (c *UserCreated) SetEventMeta(m event.Meta) {
if c != nil {
c.eventMeta = m
}
}
Reading Events from EventStore
With a domain object that implements the event.Aggregate
the event store client can load events and apply them using the Load(ctx, agg)
method.
// GetUser populates an user from event store.
func (rw *User) GetUser(ctx context.Context, userID string) (*domain.User, error) {
user := &domain.User{Identity: userID}
err := rw.es.Load(ctx, user)
if err != nil {
if err != nil {
if errors.Is(err, eventstore.ErrStreamNotFound) {
return user, ErrNotFound
}
return user, err
}
return nil, err
}
return user, err
}
OnX Commands
An OnX command will validate the state of the domain object can have the command performed on it. If it can be applied it raises the event using event.Raise() Otherwise it returns an error.
// OnCreate raises an UserCreated event to create the user.
// Note: The handler will check that the user does not already exsist.
func (a *User) OnCreate(identity string) error {
event.Raise(a, &UserCreated{Identity: identity})
return nil
}
// OnScored will attempt to score a task.
// If the task is not in a Created state it will fail.
func (a *Task) OnScored(taskID string, score int64, attributes Attributes) error {
if a.State != TaskStateCreated {
return fmt.Errorf("task expected created, got %s", a.State)
}
event.Raise(a, &TaskScored{TaskID: taskID, Attributes: attributes, Score: score})
return nil
}
Crud Operations for OnX Commands
The following functions in the aggregate service can be used to perform creation and updating of aggregates. The Update function will ensure the aggregate exists, where the Create is intended for non-existent aggregates. These can probably be combined into one function.
// Create is used when the stream does not yet exist.
func (rw *User) Create(
ctx context.Context,
identity string,
fn func(*domain.User) error,
) (*domain.User, error) {
session, err := rw.GetUser(ctx, identity)
if err != nil && !errors.Is(err, ErrNotFound) {
return nil, err
}
if err = fn(session); err != nil {
return nil, err
}
_, err = rw.es.Save(ctx, session)
return session, err
}
// Update is used when the stream already exists.
func (rw *User) Update(
ctx context.Context,
identity string,
fn func(*domain.User) error,
) (*domain.User, error) {
session, err := rw.GetUser(ctx, identity)
if err != nil {
return nil, err
}
if err = fn(session); err != nil {
return nil, err
}
_, err = rw.es.Save(ctx, session)
return session, err
}
Progress! so i have moved into working on aggregates. Which are a grouping of events that replayed on an object set the current state of the object. I came up with this little bit of generic wonder.
type PA[T any] interface {
event.Aggregate
*T
}
// Create uses fn to create a new aggregate and store in db.
func Create[A any, T PA[A]](ctx context.Context, es *EventStore, streamID string, fn func(context.Context, T) error) (agg T, err error) {
ctx, span := logz.Span(ctx)
defer span.End()
agg = new(A)
agg.SetStreamID(streamID)
if err = es.Load(ctx, agg); err != nil {
return
}
if err = event.NotExists(agg); err != nil {
return
}
if err = fn(ctx, agg); err != nil {
return
}
var i uint64
if i, err = es.Save(ctx, agg); err != nil {
return
}
span.AddEvent(fmt.Sprint("wrote events = ", i))
return
}
This lets me do something like this:
a, err := es.Create(ctx, r.es, streamID, func(ctx context.Context, agg *domain.SaltyUser) error {
return agg.OnUserRegister(nick, key)
})
I can tell the function the type being modified and returned using the function argument that is passed in. pretty cray cray.
With respect to logging.. oh man.. it really depends on the environment you are working in.. development? log everything! and use a jeager open trace for the super gnarly places. So you can see whats going on while building. But, for production? metrics are king. I donāt want to sift through thousands of lines but have a measure that can tell me the health of the service.
@prologic@twtxt.net Error handling especially in Go is very tricky I think. Even though the idea is simple, itās fairly hard to actually implement and use in a meaningful way in my opinion. All this error wrapping or the lack of it and checking whether some specific error occurred is a mess. errors.As(ā¦)
just doesnāt feel natural. errors.Is(ā¦)
only just. I mainly avoided it. Yesterday evening I actually researched a bit about that and found this article on errors with Go 1.13. It shed a little bit of light, but I still have a long way to go, I reckon.
We tried several things but havenāt found the holy grail. Currently, we have a mix of different styles, but nothing feels really right. And having plenty of different approaches also doesnāt help, thatās right. I agree, error messages often end up getting wrapped way too much with useless information. We havenāt found a solution yet. We just noticed that it kind of depends on the exact circumstances, sometimes the caller should add more information, sometimes itās better if the callee already includes what it was supposed to do.
To experiment and get a feel for yesterdayās research results I tried myself on the combined log parser and how to signal three different errors. Iām not happy with it. Any feedback is highly appreciated. The idea is to let the caller check (not implemented yet) whether a specific error occurred. That means I have to define some dedicated errors upfront (ErrInvalidFormat
, ErrInvalidStatusCode
, ErrInvalidSentBytes
) that can be used in the err == ErrInvalidFormat
or probably more correct errors.Is(err, ErrInvalidFormat)
check at the caller.
All three errors define separate error categories and are created using errors.New(ā¦)
. But for the invalid status code and invalid sent bytes cases I want to include more detail, the actual invalid number that is. Since these errors are already predefined, I cannot add this dynamic information to them. So I would need to wrap them Ć la fmt.Errorf("invalid sent bytes '%s': %w", sentBytes, ErrInvalidSentBytes")
. Yet, the ErrInvalidSentBytes
is wrapped and can be asserted later on using errors.Is(err, ErrInvalidSentBytes)
, but the big problem is that the message is repeated. I donāt want that!
Having a Python and Java background, exception hierarchies are a well understood concept Iām trying to use here. While typing this long message it occurs to me that this is probably the issue here. Anyways, I thought, I just create a ParseError
type, that can hold a custom message and some causing error (one of the three ErrInvalid*
above). The custom message is then returned at Error()
and the wrapped cause will be matched in Is(ā¦)
. I then just return a ParseError{fmt.Sprintf("invalid sent bytes '%s'", sentBytes), ErrInvalidSentBytes}
, but that looks super weird.
I probably need to scrap the āparent errorā ParseError
and make all three āsuberrorsā three dedicated error types implementing Error() string
methods where I create a useful error messages. Then the caller probably could just errors.Is(err, InvalidSentBytesError{})
. But creating an instance of the InvalidSentBytesError
type only to check for such an error category just does feel wrong to me. However, it might be the way to do this. I donāt know. To be tried. Opinions, anyone? Implementing a whole new type is some effort, that I want to avoid.
Alternatively just one ParseError
containing an error kind enumeration for InvalidFormat
and friends could be used. Also seen that pattern before. But that would then require the much more verbose var parseError ParseError; if errors.As(err, &parseError) && parseError.Kind == InvalidSentBytes { ⦠}
or something like that. Far from elegant in my eyes.
I have updated my eventDB to have subscriptions! It now has websockets like msgbus. I have also added a in memory store that can be used along side the disk backed wal.
I did a take home software engineering test for a company recently, unfortunately I was really sick (have finally recovered) at the time š¢ I was also at the same time interviewing for an SRE position (as well as Software Engineering).
Got the results of my take-home today and whilst there was some good feedback, man the criticisms of my work were harsh. Iām strictly not allowed to share the work I did for this take-home test, and I really can only agree with the āno unit testsā piece of the feedback, I could have done better there, but I was time pressured, sick and ran out of steam. I was using a lot of libraires to do the work so in the end found it difficult to actually think about a proper set of āUnit Testsā. I did write one (in shell) but I guess it wasnāt seen?
The other points were on my report and future work. Not detailed enough I guess? Hmmm š¤
Am I really this bad? Does my code suck? š¤ Have I completely lost touch with software engineering? š¤¦āāļø
Hi, I am playing with making an event sourcing database. Its super alpha but I thought I would share since others are talking about databases and such.
Itās super basic. Using tidwall/wal as the disk backing. The first use case I am playing with is an implementation of msgbus. I can post events to it and read them back in reverse order.
I plan to expand it to handle other event sourcing type things like aggregates and projections.
Find it here: sour-is/ev
@prologic@twtxt.net @movq@www.uninformativ.de @lyse@lyse.isobeef.org
Iām trying to switch from Konversation to irssi. Letās see how that goes. Any irssiers out there who can recommend specific settings or scripts? I already got myself trackbar.pl
and nickcolor.pl
as super-essentials. Also trying window_switcher.pl
. Somehow my custom binds for Ctrl+1/2/3/etc.
to switch to window 1/2/3/etc. doesnāt do anything: { key = "^1"; id = "change_window"; data = "1"; }
(I cannot use the default with Alt
as this is handled by my window manager). Currently, Iām just cycling with Ctrl+N/P
. Other things to solve in the near future:
- better, more colorful and compact theme (just removed clock from statusbar so far)
- getting bell/urgency hints working on arriving messages
- nicer tabs in status bar, maybe even just channel names and no indexes
- decluster status bar with user and channel modes (I never cared about those in the last decade)
Hmm, @prologic@twtxt.net / @lyse@lyse.isobeef.org: Should we remove the section āTraditional Human-Readable Topicsā from the spec? Or mark is as deprecated? I havenāt seen this being used in the wild for years. š¤
@chronolink@chrono.tilde.cafe Replies are not part of the original twtxt format. They were added later as an extension by Yarn.social: https://dev.twtxt.net/doc/twtsubjectextension.html (only the section āMachine-Parsable Conversation Groupingā is used these days)
@prologic@twtxt.net Oh.. reading comprehension is strong today.. you went to US and now back.
We moved to the US. When we got back we just never got new chickens again š¢
@movq@www.uninformativ.de I usually only use eggs for baking or fry them for potatoes and spinach. @prologic@twtxt.net Why donāt you have them anymore? Did the fox get them all when the door didnāt close in time? ]:->
Types of Scopes
ā Read more
Field Topology
ā Read more
Health Data
ā Read more
@mutefall@twtxt.net interesting.. were you working on one of the two universities that used it between 1989 and 1991?
#live #music idea: revived devices used as instruments, #sound coming from their own speakers. #livecoding #permacomputing #666cpu #uxn
Salary Negotiation
ā Read more
idea: using a #matrixmixer to generate feedback exciting membranes with a piezo. Try an irish drum #minijack #video #sound
@prologic@twtxt.net yeah. For commercial use even. Just need to put an attribution note in the project README
@xuu@txt.sour.is So that means weāre allowed to use it right? š
@lyse@lyse.isobeef.org Excellent use of old denim, and also excellent use of long-form twt!