tiny_bus 2.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/lib/tiny_bus.rb +44 -31
  3. metadata +20 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 523256349438598095486d3bcdff8fc0df0c5c2375fef06402bd2385cbeb14e6
4
- data.tar.gz: e832821fc355d7bf69a5378601bbc90c16439d683583b189d1f07be8481eccdf
3
+ metadata.gz: 33fd7e7b558ef0142c5edccdd2f680edd44b1b857a9bd6b39c6c6c554b3fc007
4
+ data.tar.gz: 75c523e3d559488de425a0ebe96c2e36253d85e226f2cc83de35c72bc56254b3
5
5
  SHA512:
6
- metadata.gz: f7dc510d91314737850e511ab0e20b3c664ee8950766880a28b128f9b518fb0188acd0bd0c735ec7834c4c78650809f527352c42477fadaba1427f9c2714b127
7
- data.tar.gz: fb351e69dacf513efc9198e67a3f6a5fd602f2d7924bcf7f1d3e5a9dfe62116892f360519d7863f6294b8aba65e56c9ee50796d7c6b7f9c5f68c4d8cf8494551
6
+ metadata.gz: 8e12dff36fe08ad554645939c3b5509e9f8718bbb4eedc4819a1dbb53460093459c1e77fae52b1dd6b20ae4b25fe840a06651c446c46b8dcfac81c0a8b9a065e
7
+ data.tar.gz: f30197ba6ef9502d38066d6223789c2c6dccdf704abc289b1f022e8cbc8aa75bcd14ea5c3553546d5e7ca5bb9d8f1ec63370da167d55fb576d79f1373bc73fe5
data/lib/tiny_bus.rb CHANGED
@@ -2,6 +2,7 @@ require 'time'
2
2
  require 'set'
3
3
  require 'securerandom'
4
4
  require 'tiny_log'
5
+ require 'tiny_pipe'
5
6
 
6
7
  # NOTE: This library depends on the TinyLog library.
7
8
  #
@@ -11,20 +12,20 @@ require 'tiny_log'
11
12
  # - msgs can enter the TinyBus via the #msg method
12
13
  #
13
14
  # The messages that come into this TinyBus are assumed to be Hash-like, in the
14
- # sense that they have a 'topic' key that can be accessed using Hash-like key
15
- # access syntax, and that the 'topic' key will serve as the method of
15
+ # sense that they have a '.topic' key that can be accessed using Hash-like key
16
+ # access syntax, and that the '.topic' key will serve as the method of
16
17
  # distribution.
17
18
  #
18
19
  # Usage:
19
20
  # t = TinyBus.new
20
21
  # t.sub('news', <some object that responds to #msg)
21
- # t.msg({'topic' => 'news', 'details' => 'Historic happenings!}) # goes to 'news' subscribers
22
- # t.msg({'topic' => 'whatever', 'details' => 'Historic happenings!}) # goes to dead letter output, or raises exception, depending on the configuration
22
+ # t.msg({'.topic' => 'news', 'details' => 'Historic happenings!}) # goes to 'news' subscribers
23
+ # t.msg({'.topic' => 'whatever', 'details' => 'Historic happenings!}) # goes to dead letter output, or raises exception, depending on the configuration
23
24
  #
24
25
  # Initialization options:
25
- # TinyBus.new(log: <a filename for log output>) # will log all normal msgs in this file
26
- # TinyBus.new(dead: <a filename for dead message log output>) # will log all undeliverable msgs in this file
27
- # TinyBus.new(raise_on_dead: true) # strict mode for undeliverable messages, defaults to false
26
+ # TinyBus.new(log: <a TinyLog for output>) # will log all normal msgs in this file
27
+ # TinyBus.new(dead: <a TinyLog for dead messages>) # will log all undeliverable msgs in this file
28
+ # TinyBus.new(raise_on_dead: true) # strict mode for undeliverable messages, defaults to false
28
29
  class TinyBus
29
30
  # log:
30
31
  # if specified, it should be a TinyLog instance
@@ -32,12 +33,28 @@ class TinyBus
32
33
  # dead:
33
34
  # if specified, it should be a TinyLog instance
34
35
  # if not specified, it will create a new TinyLog instance for $stderr
36
+ # translator:
37
+ # the translator is an instance of TinyPipe, if you want to translate the
38
+ # incoming masssage (i.e. annotate with additional fields, change
39
+ # keys/values on incoming messges). if not specified no translatioins will
40
+ # be made on incoming messages other than the default annotations
41
+ # NOTE: all messages are automatically annotated with three fields:
42
+ # - .time: the Unix time the message is received in Integer milliseconds,
43
+ # - .msg_uuid: a unique UUID for the incoming message
44
+ # - .trace: a unique UUID for chains of messages (if not present)
35
45
  # raise_on_dead:
36
- # kind of a strict mode. if false, then messages with a topic with no
37
- # subscribers will go to the dead file. if true, then messages with a topic
38
- # with no subscribers will raise an exception.
39
- def initialize(log: nil, dead: nil, raise_on_dead: false)
46
+ # kind of a strict mode. if false, then messages with a '.topic' with no
47
+ # subscribers will go to the dead file. if true, then messages with a
48
+ # '.topic' with no subscribers will raise an exception.
49
+ def initialize(log: nil, dead: nil, translator: nil, raise_on_dead: false)
40
50
  @subs = {}
51
+ @translator = translator
52
+ @annotator = TinyPipe.new([
53
+ ->(m){ m['.time'] = (Time.now.to_f * 1000).to_i; m },
54
+ ->(m){ m['.msg_uuid'] = SecureRandom.uuid; m },
55
+ ->(m){ m['.trace'] ||= SecureRandom.uuid; m }
56
+ ])
57
+
41
58
  @stats = { '.dead' => 0 }
42
59
  @log = log || TinyLog.new($stdout)
43
60
  @dead = dead || TinyLog.new($stderr)
@@ -45,54 +62,52 @@ class TinyBus
45
62
  end
46
63
 
47
64
  # adds a subscriber to a topic
48
- #
49
- # topics can be any string that doesn't start with a dot (.) - dot topics are
50
- # reserved for internal TinyBus usage, such as:
51
- # - .log
52
65
  def sub(topic, subber)
53
- raise TinyBus::SubscriptionToDotTopicError.new("Cannot subscribe to dot topic `#{topic}', because those are reserved for internal use") if topic.start_with?('.')
54
66
  raise TinyBus::SubscriberDoesNotMsg.new("The specified subscriber type `#{subber.class.inspect}' does not respond to #msg") unless subber.respond_to?(:msg)
55
67
 
56
68
  @subs[topic] ||= Set.new
57
69
  @subs[topic] << subber
58
70
  @stats[topic] ||= 0
71
+
72
+ msg({ '.topic' => 'sub', 'to_topic' => topic, 'subber' => subber.to.s })
59
73
  end
60
74
 
61
75
  # removes a subscriber from a topic
62
76
  def unsub(topic, subber)
63
77
  @subs[topic]&.delete(subber)
78
+
79
+ msg({ '.topic' => 'unsub', 'from_topic' => topic, 'subber' => subber.to.s })
64
80
  end
65
81
 
66
82
  # takes an incoming message and distributes it to subscribers
67
83
  #
68
- # this method also annotates incoming messages with two dot properties:
69
- # - .time: the current timestamp, accurate to the microsecond
70
- # - .msg_uuid: a UUID to uniquely identify this message
84
+ # msg: the incoming message to be distributed
85
+ # lvl (optional): the logging level
71
86
  #
72
87
  # NOTE: it modifies the incoming msg object in place in order to avoid
73
88
  # unnecessary object allocations
89
+ #
90
+ # NOTE: keys that begin with dot (.), such as '.time' are reserved for
91
+ # TinyBus and show not be altered by outside code, otherwise undefined
92
+ # behavior may result.
74
93
  def msg(msg, lvl='info')
75
- topic = msg['topic']
94
+ msg = @annotator.pipe(msg)
95
+ msg = @translator&.pipe(msg) || msg
76
96
 
77
- raise TinyBus::SendToDotTopicError.new("Cannot send to dot topic `#{topic}', because those are reserved for internal use") if topic.start_with?('.')
97
+ topic = msg['topic']
78
98
 
79
99
  subbers = @subs[topic]
80
100
 
81
- annotated = msg.merge!({
82
- '.time' => Time.now.utc.iso8601(6),
83
- '.msg_uuid' => SecureRandom.uuid
84
- })
85
-
86
101
  if subbers
87
102
  @stats[topic] += 1
88
- subbers.each{|s| s.msg(annotated) }
89
- @log.sent annotated
103
+ subbers.each{|s| s.msg(msg) }
104
+ @log.sent msg
90
105
  else
91
106
  if @raise_on_dead
92
107
  raise TinyBus::DeadMsgError.new("Could not deliver message to topic `#{topic}'")
93
108
  else
94
109
  @stats['.dead'] += 1
95
- @dead.dead annotated
110
+ @dead.dead msg
96
111
  end
97
112
  end
98
113
  end
@@ -108,6 +123,4 @@ class TinyBus
108
123
  end
109
124
 
110
125
  class TinyBus::DeadMsgError < RuntimeError; end
111
- class TinyBus::SubscriptionToDotTopicError < RuntimeError; end
112
126
  class TinyBus::SubscriberDoesNotMsg < RuntimeError; end
113
- class TinyBus::SendToDotTopicError< RuntimeError; end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tiny_bus
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeff Lunt
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-11-19 00:00:00.000000000 Z
11
+ date: 2022-11-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: tiny_log
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: tiny_pipe
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
27
41
  description: a tiny pubsub message bus with almost no features
28
42
  email: jefflunt@gmail.com
29
43
  executables: []
@@ -35,7 +49,7 @@ homepage: https://github.com/jefflunt/tiny_bus
35
49
  licenses:
36
50
  - MIT
37
51
  metadata: {}
38
- post_install_message:
52
+ post_install_message:
39
53
  rdoc_options: []
40
54
  require_paths:
41
55
  - lib
@@ -50,8 +64,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
50
64
  - !ruby/object:Gem::Version
51
65
  version: '0'
52
66
  requirements: []
53
- rubygems_version: 3.0.3.1
54
- signing_key:
67
+ rubygems_version: 3.3.7
68
+ signing_key:
55
69
  specification_version: 4
56
70
  summary: want to have an in-memory message bus that takes hash-like objects and distributes
57
71
  them out to subscribers based on a 'topic' key, with logging to $stdout or a file,