tiny_bus 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/tiny_bus.rb +104 -0
- metadata +59 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: efb4754b66b36fa51ecd847ef7a0a92b8ef884c662e50d51cb3a1353d5fe98dc
|
4
|
+
data.tar.gz: dc6c393864d50f47131e7efc4dd2ec90781b5552ef68a452b4d0a92998dceb88
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4275c81f97f72fca9aa1d33a8cdff08afc70392539b3d98096a7878118c69f7eabd5fbb9e336c76a3adf357130ec492ce78715aed3edfe6cb83d83e605ec4e68
|
7
|
+
data.tar.gz: 99dc348ba1e2d1281da1bd84976a6b54bb14792633f93a36e4bf507cc7fe5e1a48faae79a2d24fd6e9fd15c88c5dbcf1533b6c72e264ffb41a751e88ae432580
|
data/lib/tiny_bus.rb
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'time'
|
2
|
+
require 'set'
|
3
|
+
require 'securerandom'
|
4
|
+
require 'tiny_log'
|
5
|
+
|
6
|
+
# This class implements a very simpler PubSub system where:
|
7
|
+
# - subscribers can subscribe via the #sub method
|
8
|
+
# - subscribers can unsubscribe via the #unsub method
|
9
|
+
# - msgs can enter the MsgBus via the #msg method
|
10
|
+
#
|
11
|
+
# The messages that come into this MsgBus are assumed to be Hash-like, in the
|
12
|
+
# sense that they have a 'topic' key that can be accessed using Hash-like key
|
13
|
+
# access syntax, and that the 'topic' key will serve as the method of
|
14
|
+
# distribution.
|
15
|
+
#
|
16
|
+
# Usage:
|
17
|
+
# mb = MsgBus.new
|
18
|
+
# mb.sub('news', <some object that responds to #msg)
|
19
|
+
# mb.msg({'topic' => 'news', 'details' => 'Historic happenings!}) # goes to 'news' subscribers
|
20
|
+
# mb.msg({'topic' => 'whatever', 'details' => 'Historic happenings!}) # goes to dead letter output, or raises exception, depending on the configuration
|
21
|
+
#
|
22
|
+
# Initialization options:
|
23
|
+
# MsgBus.new(log: <some object that responds to #puts>) # will send a copy of all successful messages to the log
|
24
|
+
# MsgBus.new(dead: <some object that responds to #puts>) # will send a copy of all unsuccessful messages to the dead object
|
25
|
+
# MsgBus.new(raise_on_dead: true) # strict mode for undeliverable messages, defaults to false
|
26
|
+
class TinyBus
|
27
|
+
# log:
|
28
|
+
# if specified it should be a valid filename
|
29
|
+
# if not specified will default to $stdout
|
30
|
+
# dead:
|
31
|
+
# if specified it should be a valid filename for dead letter logging
|
32
|
+
# if not specified will default to $stderr
|
33
|
+
# raise_on_dead:
|
34
|
+
# kind of a strict mode. if false, then messages with a topic with no
|
35
|
+
# subscribers will go to the dead file. if true, then messages with a topic
|
36
|
+
# with no subscribers will raise an exception.
|
37
|
+
def initialize(log: nil, dead: nil, raise_on_dead: false)
|
38
|
+
@subs = {}
|
39
|
+
@stats = { '.dead' => 0 }
|
40
|
+
@log = log ? TinyLog.new(log) : $stdout
|
41
|
+
@dead = dead ? File.open(dead, 'a') : $stderr
|
42
|
+
@raise_on_dead = raise_on_dead
|
43
|
+
end
|
44
|
+
|
45
|
+
# adds a subscriber to a topic
|
46
|
+
#
|
47
|
+
# topics can be any string that doesn't start with a dot (.) - dot topics are
|
48
|
+
# reserved for internal MsgBus usage, such as:
|
49
|
+
# - .log
|
50
|
+
def sub(topic, subber)
|
51
|
+
raise SubscriptionToDotTopicError.new("Cannot subscribe to dot topic `#{topic}', because these are reserved for internal use") if topic.start_with?('.')
|
52
|
+
raise SubscriberDoesNotMsg.new("The specified subscriber type `#{subber.class.inspect}' does not respond to #msg") unless subber.respond_to?(:msg)
|
53
|
+
|
54
|
+
@subs[topic] ||= Set.new
|
55
|
+
@subs[topic] << subber
|
56
|
+
@stats[topic] ||= 0
|
57
|
+
end
|
58
|
+
|
59
|
+
# removes a subscriber from a topic
|
60
|
+
def unsub(topic, subber)
|
61
|
+
@subs[topic]&.delete(subber)
|
62
|
+
end
|
63
|
+
|
64
|
+
# takes an incoming message and distributes it to subscribers
|
65
|
+
#
|
66
|
+
# this method also annotates incoming messages with two dot properties:
|
67
|
+
# - .time: the current timestamp, accurate to the microsecond
|
68
|
+
# - .msg_uuid: a UUID to uniquely identify this message
|
69
|
+
#
|
70
|
+
# NOTE: it modifies the incoming msg object in place in order to avoid
|
71
|
+
# unnecessary object allocations
|
72
|
+
def msg(msg)
|
73
|
+
t = msg['topic']
|
74
|
+
subbers = @subs[t]
|
75
|
+
|
76
|
+
annotated = msg.merge!({
|
77
|
+
'.time' => Time.now.utc.iso8601(6),
|
78
|
+
'.msg_uuid' => SecureRandom.uuid
|
79
|
+
})
|
80
|
+
|
81
|
+
if subbers
|
82
|
+
@stats[t] += 1
|
83
|
+
subbers.each{|s| s.msg(annotated) }
|
84
|
+
@log.puts annotated
|
85
|
+
else
|
86
|
+
if @raise_on_dead
|
87
|
+
raise DeadMsgException.new("Could not deliver message to topic `#{t}'")
|
88
|
+
else
|
89
|
+
@stats['.dead'] += 1
|
90
|
+
@dead.puts annotated
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def to_s
|
96
|
+
<<~DEBUG
|
97
|
+
MsgBus stats: #{@subs.keys.length > 0 ? "\n " + @stats.keys.sort.map{|t| "#{t.rjust(12)}: #{@stats[t]}" }.join("\n ") : '<NONE>'}
|
98
|
+
DEBUG
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
class DeadMsgError < RuntimeError; end
|
103
|
+
class SubscriptionToDotTopicError < RuntimeError; end
|
104
|
+
class SubscriberDoesNotMsg < RuntimeError; end
|
metadata
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tiny_bus
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jeff Lunt
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-11-18 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: tiny_log
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
description: want to have an in-memory message bus that takes hash-like objects and
|
28
|
+
distributes them out to subscribers based on a 'topic' key, with logging to $stdout
|
29
|
+
or a file, and absolutely nothing else? then this library is for you
|
30
|
+
email: jefflunt@gmail.com
|
31
|
+
executables: []
|
32
|
+
extensions: []
|
33
|
+
extra_rdoc_files: []
|
34
|
+
files:
|
35
|
+
- lib/tiny_bus.rb
|
36
|
+
homepage: https://github.com/jefflunt/tiny_bus
|
37
|
+
licenses:
|
38
|
+
- MIT
|
39
|
+
metadata: {}
|
40
|
+
post_install_message:
|
41
|
+
rdoc_options: []
|
42
|
+
require_paths:
|
43
|
+
- lib
|
44
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
requirements: []
|
55
|
+
rubygems_version: 3.0.3.1
|
56
|
+
signing_key:
|
57
|
+
specification_version: 4
|
58
|
+
summary: a tiny pubsub message bus with almost no features
|
59
|
+
test_files: []
|