• Stars
    star
    483
  • Rank 91,050 (Top 2 %)
  • Language
    Go
  • License
    Other
  • Created about 9 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Emits events in Go way, with wildcard, predicates, cancellation possibilities and many other good wins

Emitter wercker status Coverage Status godoc

The emitter package implements a channel-based pubsub pattern. The design goals are to use Golang concurrency model instead of flat callbacks and to design a very simple API that is easy to consume.

Why?

Go has expressive concurrency model but nobody uses it properly for pubsub as far as I can tell (in the year 2015). I implemented my own solution as I could not find any other that meets my expectations. Please, read this article for more information.

What it does?

Brief example

e := &emitter.Emitter{}
go func(){
	<-e.Emit("change", 42) // wait for the event sent successfully
	<-e.Emit("change", 37)
	e.Off("*") // unsubscribe any listeners
}()

for event := range e.On("change") {
	// do something with event.Args
	println(event.Int(0)) // cast the first argument to int
}
// listener channel was closed

Constructor

emitter.New takes a uint as the first argument to indicate what buffer size should be used for listeners. It is also possible to change the buffer capacity during runtime using the following code: e.Cap = 10.

By default, the emitter uses one goroutine per listener to send an event. You can change that behavior from asynchronous to synchronous by passing emitter.Sync flag as shown here: e.Use("*", emitter.Sync). I recommend specifying middlewares(see below) for the emitter at the begining.

Wildcard

The package allows publications and subscriptions with wildcard. This feature is based on path.Match function.

Example:

go e.Emit("something:special", 42)
event := <-e.Once("*") // search any events
println(event.Int(0)) // will print 42

// or emit an event with wildcard path
go e.Emit("*", 37) // emmit for everyone
event := <-e.Once("something:special")
println(event.Int(0)) // will print 37

Note that the wildcard uses path.Match, but the lib does not return errors related to parsing for this is not the main feature. Please check the topic specifically via emitter.Test() function.

Middlewares

An important part of pubsub package is the predicates. It should be allowed to skip some events. Middlewares address this problem. The middleware is a function that takes a pointer to the Event as its first argument. A middleware is capable of doing the following items:

  1. It allows you to modify an event.
  2. It allows skipping the event emitting if needed.
  3. It also allows modification of the event's arguments.
  4. It allows you to specify the mode to describe how exactly an event should be emitted(see below).

There are two ways to add middleware into the event emitting flow:

  • via .On("event", middlewares...)
  • via .Use("event", middlewares...)

The first one add middlewares only for a particular listener, while the second one adds middlewares for all events with a given topic.

For example:

// use synchronous mode for all events, it also depends
// on the emitter capacity(buffered/unbuffered channels)
e.Use("*", emitter.Sync)
go e.Emit("something:special", 42)

// define predicate
event := <-e.Once("*", func(ev *emitter.Event){
	if ev.Int(0) == 42 {
	    // skip sending
		ev.Flags = ev.Flags | emitter.FlagVoid
	}
})
panic("will never happen")

Flags

Flags needs to describe how exactly the event should be emitted. The available options are listed here.

Every event(emitter.Event) has a field called.Flags that contains flags as a binary mask. Flags can be set only via middlewares(see above).

There are several predefined middlewares to set needed flags:

You can chain the above flags as shown below:

e.Use("*", emitter.Void) // skip sending for any events
go e.Emit("surprise", 65536)
event := <-e.On("*", emitter.Reset, emitter.Sync, emitter.Once) // set custom flags for this listener
pintln(event.Int(0)) // prints 65536

Cancellation

Golang provides developers with a powerful control for its concurrency flow. We know the state of a channel and whether it would block a go routine or not. So, by using this language construct, we can discard any emitted event. It's a good practice to design your application with timeouts so that you cancel the operations if needed as shown below:

Assume you have time out to emit the events:

done := e.Emit("broadcast", "the", "event", "with", "timeout")

select {
case <-done:
	// so the sending is done
case <-time.After(timeout):
	// time is out, let's discard emitting
	close(done)
}

It's pretty useful to control any goroutines inside an emitter instance.

Callbacks-only usage

using the emitter in more traditional way is possible, as well. If you don't need the async mode or you very attentive to the application resources, then the recipe is to use an emitter with zero capacity or to use FlagVoid to skip sending into the listener channel and use middleware as callback:

e := &emitter.Emitter{}
e.Use("*", emitter.Void)

go e.Emit("change", "field", "value")
e.On("change", func(event *Event){
	// handle changes here
	field := event.String(0)
	value := event.String(1)
	// ...and so on
})

Groups

Group merges different listeners into one channel. Example:

e1 := &emitter.Emitter{}
e2 := &emitter.Emitter{}
e3 := &emitter.Emitter{}

g := &emitter.Group{Cap: 1}
g.Add(e1.On("first"), e2.On("second"), e3.On("third"))

for event := g.On() {
	// handle the event
	// event has field OriginalTopic and Topic
}

Also you can combine several groups into one.

See the api here.

Event

Event is a struct that contains event information. Also, th event has some helpers to cast various arguments into bool, string, float64, int by given argument index with an optional default value.

Example:

go e.Emit("*", "some string", 42, 37.0, true)
event := <-e.Once("*")

first := event.String(0)
second := event.Int(1)
third := event.Float(2)
fourth := event.Bool(3)

// use default value if not exists
dontExists := event.Int(10, 64)
// or use dafault value if type don't match
def := event.Int(0, 128)

// .. and so on

License

MIT

More Repositories

1

go-starter-kit

[abandoned] Golang isomorphic react/hot reloadable/redux/css-modules/SSR starter kit
Go
2,826
star
2

when

A natural language date/time parser with pluggable rules
Go
1,328
star
3

go-duktape

[abandoned] Duktape JavaScript engine bindings for Go
Go
777
star
4

config

JSON or YAML configuration wrapper with convenient access methods.
Go
266
star
5

cdn

[abandoned] Content Delivery Network on the top of MongoDb GridFs with on-the-fly image crop/resize
Go
132
star
6

go-tgbot

Golang telegram bot API wrapper, session-based router and middleware
Go
117
star
7

srlt

Simple tool for save and restore states of all existing repositories on the given path
Go
53
star
8

staticbin

Gin middleware/handler for serving static files from binary data
Go
43
star
9

hook-to-trello

Github & Bitbucket web hooks handler for Trello
LiveScript
28
star
10

gojax

A set of extensions for goja javascript engine
Go
27
star
11

swarm

A CRDT-backed reactive real-time data with no merge conflicts, with offline mode. For business-critical data-driven apps on intermittently connected devices.
JavaScript
27
star
12

go-gamp

Google Analytics Measurement Protocol in Golang
Go
16
star
13

gin-cache

Tiny and simple cache middleware for gin framework
Go
15
star
14

rest

REST interface over MongoDB, as middlware for martini framework.
Go
14
star
15

go-duktape-fetch

Server side fetch polyfill for go-duktape
Go
12
star
16

on

CLI for fsnotify
Go
8
star
17

chat

SwarmDB Example Chat Application
JavaScript
4
star
18

node-mystem

Node.js wrapper for `MyStem` morphology text analyzer by Yandex.ru
LiveScript
4
star
19

todo

SwarmDB Example TodoMVC Application. Demo 👉
JavaScript
3
star
20

pinentry-mac-keychain

A pinentry program for macOs that stores entered PINs in the macOS KeyChain. Convenient when use with smart cards, like Yubikey
Go
2
star
21

var

tool to fill in json/yaml stdin stream from the environment variables
Go
2
star
22

t2s

Text-To-Speech tool for Russian language, written in Golang
Go
1
star
23

pickle.js

JavaScript implementation of the Python pickle format. Fork.
JavaScript
1
star
24

dotfiles

$HOME sweet home
Vim Script
1
star
25

mice

SwarmDB Example Mice Application
JavaScript
1
star
26

go-googl

A Golang library for https://goo.gl URL shortener API
Go
1
star