tiny_bus 2.0.0 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
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,