tiny_bus 2.0.0 → 3.0.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 +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,