Glassdrive, Rádio Popular, Atum General: perdoem-me mas com equipas de nomes assim, para mim, o desporto praticado na Volta é um bocado… triste.
Glassdrive, Rádio Popular, Atum General: perdoem-me mas com equipas de nomes assim, para mim, o desporto praticado na Volta é um bocado… triste. ⌘ Read more
(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
}
(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.
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.
Make Linux look like Star Trek LCARS
That’s one seriously next generation desktop environment right there… ⌘ Read more
Planning next to your code – GitHub Projects is now generally available
Today, we are announcing the general availability of the new and improved Projects powered by GitHub Issues. GitHub Projects connects your planning directly to the work your teams are doing in GitHub and flexibly adapts to whatever your team needs at any point. ⌘ Read more
Paul Schaub: Creating a Web-of-Trust Implementation: Certify Keys with PGPainless
Currently I am working on a Web-of-Trust implementation for the OpenPGP library PGPainless. This work will be funded by the awesome NLnet foundation through NGI Assure. Check them out! NGI Assure is made possible with financial support from the European Commission’s Next Generation Internet programme.
[
ssh client, because that's me, no-matter where I am. The only exception to this rule is I usually create a separate key for any "work" / " company" I am a part of.
@prologic@twtxt.net I have seen single use keys that are signed by a central PKI .. Keybase has one that uses a chatbot to generate the keys on the fly.
It just comes down to your threat model :)
New repository: aquilax/sparkline-d - Library to generate sparkline string
@mutefall I can’t take credit for the CSS on the site, it came from my site generator :D https://kiln.adnano.co
there was a connection between one of the guys who wrote the first pickup advice in the 70s, and general semantics. i could investigate
What you need to know about macOS X 10.14 Deprecation
Docker supports Docker Desktop on the most recent versions of macOS. That is, the current release of macOS and the previous two releases. As new major versions of macOS are made generally available, Docker stops supporting the oldest version and supports the newest version of macOS (in addition to the previous two releases). Keeping with […]
The post [What you need to know about macOS X 10.14 Deprecation](https://www.docker.com/blo … ⌘ Read more
For the Sake of Simplicity
⌘ Read more
generic answer to any threat: “if the grease doesn’t get me first”
GitHub Enterprise Server 3.4 improves developer productivity and adds reusable workflows to CI/CD
The GitHub Enterprise Server 3.4 release candidate delivers enhancements to make life easier and more productive, from keyboard shortcuts to auto-generated release notes! ⌘ Read more
Monal IM: Funding campaign: Mac Mini for faster Monal development
Update 15.02.2022 Thank you very much. We reached our target of 1000€ within less than a week. We will order our new Mac mini tonight. Stay tuned for a big development blog post.
Dear Monal Community,
as you know the Monal project is developed by volunteers and has no general funding so far.
To improve the development situation it would be of advantage to have a physical build server for the developers.
This would have … ⌘ Read more
saying “without loss of generality” is a great way to hide bullshit in a proof. i have applied it many times with huge success
I should use twtxt more. I actually like it better than fedi, in general.
Hypothesis Generation
⌘ Read more
GoCN 每日新闻 (2022-01-13)
GoCN 每日新闻 (2022-01-13)
- Golang《基于 MIME 协议的邮件信息解析》部分实现https://gocn.vip/topics/20948
- 泛型可以拯救 Golang 笨拙的错误处理吗?https://blog.dnmfarrell.com/post/can-generics-rescue-golangs-clunky-error-handling/
- 更多的并行,并不等同更高的性能https://convey.earth/conversation?id=44
- 为什么 Go 有两种声明变量的方式,有什么区别,哪种好? [https://mp.weixin.qq.com/s/ADwEhSA1kFOFqzIyWvAqsA](https://mp.weixin.q … ⌘ Read more
GoCN 每日新闻 (2021-12-26)
- Go 泛型的 facilitator 模式https://rakyll.org/generics-facilititators
- Go 1.18 泛型: 好的、坏的、丑的https://itnext.io/golang-1-18-generics-the-good-the-bad-the-ugly-5e9fa2520e76
- Go 调试器发布 Delve 1.8.0 版本,支持 1.18 泛型调试https://github.com/go-delve/delve/releases/tag/v1.8.0
4 … ⌘ Read more
The Dwarf Generator Act One makes surprisingly good christmas music!
GoCN 每日新闻(2021-12-23)
- 《真·简单》Golang 轻量级桌面程序 wails 库(圣诞节限定) https://gocn.vip/topics/20899
- Go 实战 | 一文带你搞懂从单队列到优先级队列的实现https://gocn.vip/topics/20897
- 什么时候在 Go 中使用泛型https://teivah.medium.com/when-to-use-generics-in-go-36d49c1aeda
- 深入研究 NSO 零点击 iMessage 漏洞:远� … ⌘ Read more
教程:如何开始使用泛型
教程:如何开始使用泛型- 原文地址: https://go.dev/doc/tutorial/generics
- 原文作者:go.dev
- 本文永久链接: https://github.com/gocn/translator/blob/master/2021/w49_Tutorial_Getting_started_with_generics.md
- 译者: zxmfke
- 校对: cvley
目�� … ⌘ Read more
Instead of banging my head on Advent of Code, I coded something useful today: Text-to-Speech for GoBlog using the official Google Text-to-Speech API. When posting, an audio file is generated automatically from the post content. And, I have to praise myself for this, I documented the feature as well! 😄 ⌘ Read more
Agreed on “aggressive” — as a general rule, I don’t think most folks are acting like we’re in the kind of crisis we are.
fake english word generation for Go and CLI: [[https://github.com/nwtgck/go-fakelish]] #links
GitHub Enterprise Server 3.3 is generally available
GitHub Enterprise Server is now generally available for all customers. This release improves performance for CI/CD and for customers with large repositories. ⌘ Read more
GitHub Externships: enabling India’s next generation of developers
Are you a student in India? Applications are open for the GitHub Externships Winter Cohort! ⌘ Read more
GitHub Actions: reusable workflows is generally available
DRY your Actions configuration with reusable workflows (and more!) ⌘ Read more
Secure deployments with OpenID Connect & GitHub Actions now generally available
GitHub Actions now supports OpenID Connect for secure deployment to different cloud providers via short-lived, auto-rotated tokens. ⌘ Read more
JavaScript : web apps
wut?! 😳 seriously?! 🤦♂️
Python : small tools
Okay 👌
Go: micro services
Umm bad generalization 🤣 – Example yarnd that powers most of Yarn.social 😂
Java: enterprise software
Yes! Oh gawd yes! 🤣 And Java™ needs to die a swift death!
C: crimes
Hmmm? 🤔 I feel this one is going to have some backslash and/or go the way of “Hacker” being misconstrued to mean entirely different/incorrect things as is what’s happening in the media (for various definitions of “media”).
@adi@f.adi.onl Ugh sorry for not replying. If the file list is dynamic, usually you use something like autoconf to generate the Makefile. I’ve also used wildcards in the past and that works okay. You should be able to use shell commands to populate the file list.
Third generation of AirPods. So, new AirPods. Read more at Apple. 😂
Apple’s event on Monday is bringing, as always, speculation to the table. One thing most outlets seem to agree is the introduction of an “M1X” chip, thought Apple might call it differently. M1X might also mean, M1(we don’t know what comes after, or next generation). Either way, I would really like to see the return of the 27” iMac, but I will not hold my breath. Nevertheless, Monday is going to be an exciting day for many, including me! 🍎
Everybody is building one because, you know, why not? Why I built my own static site generator.
Test to generate more logging.
GitHub security update: revoking weakly-generated SSH keys
On September 28, 2021, we received notice from the developer Axosoft regarding a vulnerability in a dependency of their popular git GUI client - GitKraken. An underlying issue with a dependency, called `keypair`, resulted in the GitKraken client generating weak SSH keys. ⌘ Read more
A new public beta of GitHub Releases: How we’re improving the release experience
GitHub Releases has a new look and updated tools to make it easier for open source communities to create and share high-quality releases with auto-generated release notes. ⌘ Read more
new algo in #sndkit @!(sndkitref “sparse”)!@ is a sparse noise generator, similar to velvet noise, that produces a random series of impulses at a specified rate in Hz.
Enterprise managed users are now generally available for GitHub Enterprise Cloud
Manage your company in the cloud with more control and governance using enterprise managed users. ⌘ Read more
GitHub Enterprise Server 3.2 is now generally available
Today, we’re excited to announce that GitHub Enterprise Server 3.2 is generally available. This release brings over 70 new features and changes that improve developer experience and deliver new security capabilities. ⌘ Read more
Peter Saint-Andre: Opinions Weak and Strong
Continuing a thread that I started to explore earlier this year, I’d like to take a closer look at the intensity of opinions. Here as almost everywhere, there is a continuum: we all have opinions we hold strongly and opinions we hold weakly. Not only do the specific contents of these buckets change over time, but in general the intensity of one’s opinions can change over time, too. We’re all familiar with the sophomoric young adult who has strong opinions about everything (yes, I resemb … ⌘ Read more
What’s new from GitHub Changelog? August 2021 Recap
What did we ship in August? Codespaces, Discussions, and lots of other updates, from the general availability of the dark high contrast theme to an auto-generated table of contents for wikis. ⌘ Read more
On the blog: Age Cohorts, Ruining Things, and Apocalypses https://john.colagioia.net/blog/2021/08/15/generations.html #rant #politics
On the blog: Free Culture Book Club — Typhoon, Part 2 https://john.colagioia.net/blog/2021/08/14/typhoon2.html Generations in a Nutshell https://john.colagioia.net/blog/2021/08/15/generations.html #rant #politics
August 2021 Campus Experts applications are open!
The GitHub Campus Expert applications are officially open for the August 2021 generation! ⌘ Read more
On the blog: Developer Journal, Pachamama Raymi https://john.colagioia.net/blog/2021/08/02/generic.html #programming #project #devjournal
rational people can use very irrational people as babble generators in conversations, if the rational people are high prune (which they usually are).
GitHub Packages Container registry is generally available ⌘ Read more…
added a !projects page. it’s dynamically generated :)
First pass at sticking my twtxt in a web page. It’s not escaping all the html properly, and generally needs work, but it’s a start: http://a.9srv.net/tw/following.html
GitHub Enterprise Server 3.1 is now generally available ⌘ Read more…
I like and use the Brave Browser.It’s a free and open source browser with features like:
- Ad-blocking by default.
- Tracker-blocking by default.
- Anti-fingerprinting mechanisms to prevent you from being monitored.
- Built-in Tor windows.
- Run by a based Christian and not furry leftists.
As far as I’m concerned, Brave is indisputably the best general-purpose browser out there.There are other okay brows … ⌘ Read more
Whether to invest in stocks is moral tracks with whether general economic growth is moral.
2x3: {lives in social/physical reality}×{views things generally as positive/zero/negative sum}. To be honest, I think there’s relatively few people in social positive sum reality frames.
the ‘magic circle’ sine wave algorithm: [[https://ccrma.stanford.edu/~jos/pasp/Digital_Sinusoid_Generators.html]] #links
GitHub Advanced Security: Introducing security overview beta and general availability of secret scanning for private repositories ⌘ Read more…
@niplav@niplav.github.io I wouldn’t disagree with your characterization of people who’re interested in radical life extension, but their messaging, generally, seems to not mention “adding life to your years” at all. “Live to be 150” doesn’t sound all that great if you’re going to have 15 years of being in your 80s, so to speak.
@jlj@twt.nfld.uk “A good read: Why I find longtermism hard – […]” -> Interesting! I don’t particularly share that emotional intuition (although my bias probably cuts the other way: I am more moved by interesting projects, and more interesting problems probably also less neglected)–I generally find most problems other people find salient not very moving at all (although probably equally strongly moved by extremely near suffering compared to other people, but with a stronger emotional distance discount). EA makes sense in a very different way to me (phenomenologically, probably closest to philosophical high valence states it evokes).
GitHub Enterprise Server 3.0 is now generally available ⌘ Read more…
Neil deGrasse Tyson Explains Generational Spaceships ⌘ Read more…
Rule #1 of business: You can generate revenue to infinity, but you can only cut costs to zero. Ask HN: Is frugality underrated in startups? | Hacker News
npm 7 is now generally available! ⌘ Read more…
new algorithm in @!(ref “sndkit”)!@: @!(sndkitref “phasor”)!@ generates a normalized periodic ramp signal, typically used for table-lookup oscillators.
Mirroring my private sites on the IPFS network. Generating my static site using 11ty
a dynamically generated page for food recipes: !food
Tactic: Salty chips (Salty chips that make you buy water as a general pattern).
“Everyone who had serious philosophical conundra on that subject just, you know, died, a generation before. The Bitchun Society didn’t need to convert its detractors, just outlive them.”
New repository: aquilax/zadachko - Math template test generator.
My relationship to compliments in those days was simple: I didn’t give any and I didn’t receive any either. Talking openly about feelings - or heaven forbid - giving praise - simply wasn’t the norm. This was especially so with the older generation, people like my granddad. How to Compliment | Less Penguiny
@prologic@twtxt.net Ok.. so using NaCL boxes. yeah its just a combo of using secretbox with a generated key/nonce. and then using the pubkey box to encrypt the key/nonce for each device.
@prologic@twtxt.net Ok.. so using NaCL boxes. yeah its just a combo of using secretbox with a generated key/nonce. and then using the pubkey box to encrypt the key/nonce for each device.
@prologic@twtxt.net sender generates an AES key encrypts message. gets the device list for user and encrypts key for each device. sends the encryptedkeys+cypertext.
@prologic@twtxt.net sender generates an AES key encrypts message. gets the device list for user and encrypts key for each device. sends the encryptedkeys+cypertext.
@prologic@twtxt.net for encryption. we can have browser/app generate ec25519 keypair. store the private on device and add pub to list of devices for the user on pod.
@prologic@twtxt.net for encryption. we can have browser/app generate ec25519 keypair. store the private on device and add pub to list of devices for the user on pod.
Western culture has a very unhealthy attitude towards sleep in general. There is only one socially accepted sleep pattern: Eight hours a night, in one block, starting at between 10pm and 11pm and ending between 6am and 7am. This “early-bird” rhythm is celebrated in to the point of fetishisation and held up as the goal to which all productive adults must aspire. To behave otherwise is to be lazy, slothful, and not putting forward your all It’s Time to Stop Nap-Shaming | Hacker News