tiny_bus 2.0.0 → 3.1.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 +51 -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: b04a4860504e84547cc02b406c9146d47af4e16563b7509fe2cf3152fe5b6747
4
+ data.tar.gz: 687328b251069cf527b3de27210ce626884e9e751ccc07ef7accba31a86cd2c6
5
5
  SHA512:
6
- metadata.gz: f7dc510d91314737850e511ab0e20b3c664ee8950766880a28b128f9b518fb0188acd0bd0c735ec7834c4c78650809f527352c42477fadaba1427f9c2714b127
7
- data.tar.gz: fb351e69dacf513efc9198e67a3f6a5fd602f2d7924bcf7f1d3e5a9dfe62116892f360519d7863f6294b8aba65e56c9ee50796d7c6b7f9c5f68c4d8cf8494551
6
+ metadata.gz: 42eda75efbcdbcdd006c1dcdfeb098b48b17e6aecf5f7b27b9a630d8f383ad9a01064e77f77a486e01916ca98d71e036c726e2e5568a227dd4b36d2dc8f0f52c
7
+ data.tar.gz: 7c9cef3cf505db81c954aa8666007d30a89a2b0384e5e385759e8034ee39eb63a9f7f4d3da3981f37b63c657abece8c70d0b5b48152e1b82df7e5269f2da6909
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,33 +12,56 @@ 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
30
+ ANNOTATION_PREFIX_DEFAULT = '.'
31
+
29
32
  # log:
30
33
  # if specified, it should be a TinyLog instance
31
34
  # if not specified, it will create a new TinyLog instance for $stdout
32
35
  # dead:
33
36
  # if specified, it should be a TinyLog instance
34
37
  # if not specified, it will create a new TinyLog instance for $stderr
38
+ # translator:
39
+ # the translator is an instance of TinyPipe, if you want to translate the
40
+ # incoming masssage (i.e. annotate with additional fields, change
41
+ # keys/values on incoming messges). if not specified no translatioins will
42
+ # be made on incoming messages other than the default annotations
43
+ # NOTE: all messages are automatically annotated with three fields:
44
+ # - .time: the Unix time the message is received in Integer milliseconds,
45
+ # - .msg_uuid: a unique UUID for the incoming message
46
+ # - .trace: a unique UUID for chains of messages (if not present)
35
47
  # 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)
48
+ # kind of a strict mode. if false, then messages with a '.topic' with no
49
+ # subscribers will go to the dead file. if true, then messages with a
50
+ # '.topic' with no subscribers will raise an exception.
51
+ # annotation_prefix:
52
+ # default: '.'
53
+ # if specified, the annotated message attributes ('.time', '.msg_uuid', and
54
+ # '.trace') will have the dot ('.') replaced with the specified prefix text
55
+ def initialize(log: nil, dead: nil, translator: nil, raise_on_dead: false,
56
+ annotation_prefix: ANNOTATION_PREFIX_DEFAULT)
40
57
  @subs = {}
58
+ @translator = translator
59
+ @annotator = TinyPipe.new([
60
+ ->(m){ m[annotation_prefix + 'time'] = (Time.now.to_f * 1000).to_i; m },
61
+ ->(m){ m[annotation_prefix + 'msg_uuid'] = SecureRandom.uuid; m },
62
+ ->(m){ m[annotation_prefix + 'trace'] ||= SecureRandom.uuid; m }
63
+ ])
64
+
41
65
  @stats = { '.dead' => 0 }
42
66
  @log = log || TinyLog.new($stdout)
43
67
  @dead = dead || TinyLog.new($stderr)
@@ -45,54 +69,52 @@ class TinyBus
45
69
  end
46
70
 
47
71
  # 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
72
  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
73
  raise TinyBus::SubscriberDoesNotMsg.new("The specified subscriber type `#{subber.class.inspect}' does not respond to #msg") unless subber.respond_to?(:msg)
55
74
 
56
75
  @subs[topic] ||= Set.new
57
76
  @subs[topic] << subber
58
77
  @stats[topic] ||= 0
78
+
79
+ msg({ '.topic' => 'sub', 'to_topic' => topic, 'subber' => subber.to.s })
59
80
  end
60
81
 
61
82
  # removes a subscriber from a topic
62
83
  def unsub(topic, subber)
63
84
  @subs[topic]&.delete(subber)
85
+
86
+ msg({ '.topic' => 'unsub', 'from_topic' => topic, 'subber' => subber.to.s })
64
87
  end
65
88
 
66
89
  # takes an incoming message and distributes it to subscribers
67
90
  #
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
91
+ # msg: the incoming message to be distributed
92
+ # lvl (optional): the logging level
71
93
  #
72
94
  # NOTE: it modifies the incoming msg object in place in order to avoid
73
95
  # unnecessary object allocations
96
+ #
97
+ # NOTE: keys that begin with dot (.), such as '.time' are reserved for
98
+ # TinyBus and show not be altered by outside code, otherwise undefined
99
+ # behavior may result.
74
100
  def msg(msg, lvl='info')
75
- topic = msg['topic']
101
+ msg = @annotator.pipe(msg)
102
+ msg = @translator&.pipe(msg) || msg
76
103
 
77
- raise TinyBus::SendToDotTopicError.new("Cannot send to dot topic `#{topic}', because those are reserved for internal use") if topic.start_with?('.')
104
+ topic = msg['topic']
78
105
 
79
106
  subbers = @subs[topic]
80
107
 
81
- annotated = msg.merge!({
82
- '.time' => Time.now.utc.iso8601(6),
83
- '.msg_uuid' => SecureRandom.uuid
84
- })
85
-
86
108
  if subbers
87
109
  @stats[topic] += 1
88
- subbers.each{|s| s.msg(annotated) }
89
- @log.sent annotated
110
+ subbers.each{|s| s.msg(msg) }
111
+ @log.sent msg
90
112
  else
91
113
  if @raise_on_dead
92
114
  raise TinyBus::DeadMsgError.new("Could not deliver message to topic `#{topic}'")
93
115
  else
94
116
  @stats['.dead'] += 1
95
- @dead.dead annotated
117
+ @dead.dead msg
96
118
  end
97
119
  end
98
120
  end
@@ -108,6 +130,4 @@ class TinyBus
108
130
  end
109
131
 
110
132
  class TinyBus::DeadMsgError < RuntimeError; end
111
- class TinyBus::SubscriptionToDotTopicError < RuntimeError; end
112
133
  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.1.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-12-03 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,