🧮 USERS:1 FEEDS:2 TWTS:331 ARCHIVED:36304 CACHE:1600 FOLLOWERS:13 FOLLOWING:14
**
🧮 USERS:3 FEEDS:6 TWTS:509 BLOGS:0 ARCHIVED:102326 CACHE:2474 FOLLOWERS:9 FOLLOWING:17
**
🧮 USERS:3 FEEDS:6 TWTS:509 BLOGS:0 ARCHIVED:102326 CACHE:2474 FOLLOWERS:9 FOLLOWING:17 ⌘ Read more
🧮 USERS:1 FEEDS:2 TWTS:330 ARCHIVED:36287 CACHE:1596 FOLLOWERS:13 FOLLOWING:14
**
🧮 USERS:3 FEEDS:6 TWTS:508 BLOGS:0 ARCHIVED:102240 CACHE:2463 FOLLOWERS:9 FOLLOWING:17
**
🧮 USERS:3 FEEDS:6 TWTS:508 BLOGS:0 ARCHIVED:102240 CACHE:2463 FOLLOWERS:9 FOLLOWING:17 ⌘ Read more
🧮 Users: 2, Feeds: 6, Twts: 1026, Archived: 1116347, Cache: 89202, Followers: 29, and Following: 701.
🧮 USERS:1 FEEDS:2 TWTS:329 ARCHIVED:36277 CACHE:1614 FOLLOWERS:13 FOLLOWING:14
**
🧮 USERS:3 FEEDS:6 TWTS:507 BLOGS:0 ARCHIVED:102165 CACHE:2455 FOLLOWERS:9 FOLLOWING:17
**
🧮 USERS:3 FEEDS:6 TWTS:507 BLOGS:0 ARCHIVED:102165 CACHE:2455 FOLLOWERS:9 FOLLOWING:17 ⌘ Read more
🧮 USERS:1 FEEDS:2 TWTS:328 ARCHIVED:36263 CACHE:1622 FOLLOWERS:13 FOLLOWING:14
👋 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! 🤗
**
🧮 USERS:3 FEEDS:6 TWTS:506 BLOGS:0 ARCHIVED:102071 CACHE:2446 FOLLOWERS:9 FOLLOWING:17
**
🧮 USERS:3 FEEDS:6 TWTS:506 BLOGS:0 ARCHIVED:102071 CACHE:2446 FOLLOWERS:9 FOLLOWING:17 ⌘ Read more
🧮 USERS:1 FEEDS:2 TWTS:327 ARCHIVED:36241 CACHE:1665 FOLLOWERS:13 FOLLOWING:14
I’ve realized that trying to strictly follow what is on the IndieWeb wiki won’t work well for me. Thus, I have to invent and change some things to make it work better. ⌘ 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
}
**
🧮 USERS:3 FEEDS:6 TWTS:505 BLOGS:0 ARCHIVED:102004 CACHE:2460 FOLLOWERS:9 FOLLOWING:17
**
🧮 USERS:3 FEEDS:6 TWTS:505 BLOGS:0 ARCHIVED:102004 CACHE:2460 FOLLOWERS:9 FOLLOWING:17 ⌘ Read more
🧮 USERS:1 FEEDS:2 TWTS:326 ARCHIVED:36217 CACHE:1658 FOLLOWERS:13 FOLLOWING:14
**
🧮 USERS:3 FEEDS:6 TWTS:504 BLOGS:0 ARCHIVED:101937 CACHE:2466 FOLLOWERS:9 FOLLOWING:17
**
🧮 USERS:3 FEEDS:6 TWTS:504 BLOGS:0 ARCHIVED:101937 CACHE:2466 FOLLOWERS:9 FOLLOWING:17 ⌘ Read more
The XMPP Standards Foundation: Mid Term Evaluation Updates
It’s been a month since I wrote my last blog. For those of you who have been following my blogs, thanks a lot for taking the time to read them. In this blog, I will give the updates post mid-term evaluation and the challenges that I have been facing and how I overcame some of them.
For those of you who don’t know much about GSoC, a mid-term evaluat … ⌘ Read more
🧮 USERS:1 FEEDS:2 TWTS:325 ARCHIVED:36174 CACHE:1662 FOLLOWERS:13 FOLLOWING:14
**
🧮 USERS:3 FEEDS:6 TWTS:503 BLOGS:0 ARCHIVED:101845 CACHE:2450 FOLLOWERS:9 FOLLOWING:17
**
🧮 USERS:3 FEEDS:6 TWTS:503 BLOGS:0 ARCHIVED:101845 CACHE:2450 FOLLOWERS:9 FOLLOWING:17 ⌘ Read more
🧮 USERS:1 FEEDS:2 TWTS:324 ARCHIVED:36126 CACHE:1630 FOLLOWERS:13 FOLLOWING:14
**
🧮 USERS:3 FEEDS:6 TWTS:502 BLOGS:0 ARCHIVED:101760 CACHE:2439 FOLLOWERS:9 FOLLOWING:17
**
🧮 USERS:3 FEEDS:6 TWTS:502 BLOGS:0 ARCHIVED:101760 CACHE:2439 FOLLOWERS:9 FOLLOWING:17 ⌘ Read more
🧮 USERS:1 FEEDS:2 TWTS:323 ARCHIVED:36095 CACHE:1620 FOLLOWERS:13 FOLLOWING:14
**
🧮 USERS:3 FEEDS:6 TWTS:501 BLOGS:0 ARCHIVED:101664 CACHE:2403 FOLLOWERS:9 FOLLOWING:17
**
🧮 USERS:3 FEEDS:6 TWTS:501 BLOGS:0 ARCHIVED:101664 CACHE:2403 FOLLOWERS:9 FOLLOWING:17 ⌘ Read more
🧮 USERS:1 FEEDS:2 TWTS:322 ARCHIVED:36071 CACHE:1610 FOLLOWERS:13 FOLLOWING:14
**
🧮 USERS:3 FEEDS:6 TWTS:500 BLOGS:0 ARCHIVED:101577 CACHE:2387 FOLLOWERS:9 FOLLOWING:17
**
🧮 USERS:3 FEEDS:6 TWTS:500 BLOGS:0 ARCHIVED:101577 CACHE:2387 FOLLOWERS:9 FOLLOWING:17 ⌘ Read more
🧮 USERS:1 FEEDS:2 TWTS:321 ARCHIVED:36041 CACHE:1614 FOLLOWERS:13 FOLLOWING:14
**
🧮 USERS:3 FEEDS:6 TWTS:499 BLOGS:0 ARCHIVED:101432 CACHE:2382 FOLLOWERS:9 FOLLOWING:17
**
🧮 USERS:3 FEEDS:6 TWTS:499 BLOGS:0 ARCHIVED:101432 CACHE:2382 FOLLOWERS:9 FOLLOWING:17 ⌘ Read more
🧮 USERS:1 FEEDS:2 TWTS:320 ARCHIVED:36015 CACHE:1629 FOLLOWERS:13 FOLLOWING:14
To all my feed subscribers: If you are annoyed by the TTS audio or the “Interactions” link, add a “.min” in front of the feed type in the URL. For example https://jlelse.blog/.min.rss. Thanks for following! 😄 ⌘ Read more
**
🧮 USERS:3 FEEDS:6 TWTS:498 BLOGS:0 ARCHIVED:101357 CACHE:2381 FOLLOWERS:9 FOLLOWING:17
**
🧮 USERS:3 FEEDS:6 TWTS:498 BLOGS:0 ARCHIVED:101357 CACHE:2381 FOLLOWERS:9 FOLLOWING:17 ⌘ Read more
🧮 USERS:1 FEEDS:2 TWTS:319 ARCHIVED:35977 CACHE:1621 FOLLOWERS:13 FOLLOWING:14
**
🧮 USERS:3 FEEDS:6 TWTS:497 BLOGS:0 ARCHIVED:101289 CACHE:2380 FOLLOWERS:9 FOLLOWING:17
**
🧮 USERS:3 FEEDS:6 TWTS:497 BLOGS:0 ARCHIVED:101289 CACHE:2380 FOLLOWERS:9 FOLLOWING:17 ⌘ Read more
🧮 USERS:1 FEEDS:2 TWTS:318 ARCHIVED:35951 CACHE:1608 FOLLOWERS:13 FOLLOWING:14
**
🧮 USERS:3 FEEDS:6 TWTS:496 BLOGS:0 ARCHIVED:101190 CACHE:2501 FOLLOWERS:9 FOLLOWING:17
**
🧮 USERS:3 FEEDS:6 TWTS:496 BLOGS:0 ARCHIVED:101190 CACHE:2501 FOLLOWERS:9 FOLLOWING:17 ⌘ Read more
🧮 USERS:1 FEEDS:2 TWTS:317 ARCHIVED:35790 CACHE:1611 FOLLOWERS:13 FOLLOWING:14
Ignite Realtime Blog: REST API Openfire plugin 1.9.1 released!
Woopsie doodle! It turns out that a rather annoying bug was introduced in version 1.9.0 of the REST API plugin for Openfire, that we released earlier today!
To avoid unnecessary issues, we’ve decided to follow up with an immediate new release that addresses this issue. … ⌘ Read more
**
🧮 USERS:3 FEEDS:6 TWTS:495 BLOGS:0 ARCHIVED:101094 CACHE:2507 FOLLOWERS:9 FOLLOWING:17
**
🧮 USERS:3 FEEDS:6 TWTS:495 BLOGS:0 ARCHIVED:101094 CACHE:2507 FOLLOWERS:9 FOLLOWING:17 ⌘ Read more
🧮 USERS:1 FEEDS:2 TWTS:316 ARCHIVED:35745 CACHE:1618 FOLLOWERS:13 FOLLOWING:14
**
🧮 USERS:3 FEEDS:6 TWTS:494 BLOGS:0 ARCHIVED:101009 CACHE:2498 FOLLOWERS:9 FOLLOWING:17
**
🧮 USERS:3 FEEDS:6 TWTS:494 BLOGS:0 ARCHIVED:101009 CACHE:2498 FOLLOWERS:9 FOLLOWING:17 ⌘ Read more
🧮 USERS:1 FEEDS:2 TWTS:315 ARCHIVED:35714 CACHE:1620 FOLLOWERS:13 FOLLOWING:14
**
🧮 USERS:3 FEEDS:6 TWTS:493 BLOGS:0 ARCHIVED:100918 CACHE:2478 FOLLOWERS:9 FOLLOWING:17
**
🧮 USERS:3 FEEDS:6 TWTS:493 BLOGS:0 ARCHIVED:100918 CACHE:2478 FOLLOWERS:9 FOLLOWING:17 ⌘ Read more
🧮 USERS:1 FEEDS:2 TWTS:314 ARCHIVED:35694 CACHE:1628 FOLLOWERS:13 FOLLOWING:14
**
🧮 USERS:3 FEEDS:6 TWTS:492 BLOGS:0 ARCHIVED:100822 CACHE:2465 FOLLOWERS:9 FOLLOWING:17
**
🧮 USERS:3 FEEDS:6 TWTS:492 BLOGS:0 ARCHIVED:100822 CACHE:2465 FOLLOWERS:9 FOLLOWING:17 ⌘ Read more
🧮 USERS:1 FEEDS:2 TWTS:313 ARCHIVED:35653 CACHE:1609 FOLLOWERS:13 FOLLOWING:14
**
🧮 USERS:3 FEEDS:6 TWTS:491 BLOGS:0 ARCHIVED:100750 CACHE:2522 FOLLOWERS:9 FOLLOWING:17
**
🧮 USERS:3 FEEDS:6 TWTS:491 BLOGS:0 ARCHIVED:100750 CACHE:2522 FOLLOWERS:9 FOLLOWING:17 ⌘ Read more
🧮 USERS:1 FEEDS:2 TWTS:312 ARCHIVED:35548 CACHE:1535 FOLLOWERS:13 FOLLOWING:14
**
🧮 USERS:3 FEEDS:6 TWTS:490 BLOGS:0 ARCHIVED:100677 CACHE:2524 FOLLOWERS:9 FOLLOWING:17
**
🧮 USERS:3 FEEDS:6 TWTS:490 BLOGS:0 ARCHIVED:100677 CACHE:2524 FOLLOWERS:9 FOLLOWING:17 ⌘ Read more
🧮 USERS:1 FEEDS:2 TWTS:311 ARCHIVED:35521 CACHE:1525 FOLLOWERS:13 FOLLOWING:14
**
🧮 USERS:3 FEEDS:6 TWTS:489 BLOGS:0 ARCHIVED:100592 CACHE:2507 FOLLOWERS:9 FOLLOWING:17
**
🧮 USERS:3 FEEDS:6 TWTS:489 BLOGS:0 ARCHIVED:100592 CACHE:2507 FOLLOWERS:9 FOLLOWING:17 ⌘ Read more
🧮 USERS:1 FEEDS:2 TWTS:310 ARCHIVED:35401 CACHE:1496 FOLLOWERS:13 FOLLOWING:14