tiny_bus 1.0.4 → 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 +52 -37
  3. metadata +20 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 80e736518279f690809f359a1a1c4c96fd32154d3c847b83fe249e409bf5fd56
4
- data.tar.gz: a1557163fb8fb490e79cbf159411989de8fc0e49bb8b5436d43cd7392c554031
3
+ metadata.gz: 33fd7e7b558ef0142c5edccdd2f680edd44b1b857a9bd6b39c6c6c554b3fc007
4
+ data.tar.gz: 75c523e3d559488de425a0ebe96c2e36253d85e226f2cc83de35c72bc56254b3
5
5
  SHA512:
6
- metadata.gz: eb83e912761d438bc17d93a4121e0c897c3d88b212ec50a842d088cedcaa028b84b6e797727be10a817fe63ad93e941f0f82278ffc79bea5587d13e516398e0d
7
- data.tar.gz: bcf4adbfa6c0f959410b3b25ede768690c5e2c8bf47cd28e0d0b38e3d05941af44496656a9d75ce468da66d7e456a2761141413383bfba71b3c6759a38e61c88
6
+ metadata.gz: 8e12dff36fe08ad554645939c3b5509e9f8718bbb4eedc4819a1dbb53460093459c1e77fae52b1dd6b20ae4b25fe840a06651c446c46b8dcfac81c0a8b9a065e
7
+ data.tar.gz: f30197ba6ef9502d38066d6223789c2c6dccdf704abc289b1f022e8cbc8aa75bcd14ea5c3553546d5e7ca5bb9d8f1ec63370da167d55fb576d79f1373bc73fe5
data/lib/tiny_bus.rb CHANGED
@@ -2,95 +2,112 @@ require 'time'
2
2
  require 'set'
3
3
  require 'securerandom'
4
4
  require 'tiny_log'
5
+ require 'tiny_pipe'
5
6
 
7
+ # NOTE: This library depends on the TinyLog library.
8
+ #
6
9
  # This class implements a very simpler PubSub system where:
7
10
  # - subscribers can subscribe via the #sub method
8
11
  # - subscribers can unsubscribe via the #unsub method
9
12
  # - msgs can enter the TinyBus via the #msg method
10
13
  #
11
14
  # The messages that come into this TinyBus 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
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
14
17
  # distribution.
15
18
  #
16
19
  # Usage:
17
20
  # t = TinyBus.new
18
21
  # t.sub('news', <some object that responds to #msg)
19
- # t.msg({'topic' => 'news', 'details' => 'Historic happenings!}) # goes to 'news' subscribers
20
- # 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
21
24
  #
22
25
  # Initialization options:
23
- # TinyBus.new(log: <a filename for log output>) # will log all normal msgs in this file
24
- # TinyBus.new(dead: <a filename for dead message log output>) # will log all undeliverable msgs in this file
25
- # 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
26
29
  class TinyBus
27
30
  # log:
28
- # if specified it should be a valid filename
29
- # if not specified will default to $stdout
31
+ # if specified, it should be a TinyLog instance
32
+ # if not specified, it will create a new TinyLog instance for $stdout
30
33
  # dead:
31
- # if specified it should be a valid filename for dead letter logging
32
- # if not specified will default to $stderr
34
+ # if specified, it should be a TinyLog instance
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)
33
45
  # 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)
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)
38
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
+
39
58
  @stats = { '.dead' => 0 }
40
- @log = log ? TinyLog.new(log) : TinyLog.new($stdout)
41
- @dead = dead ? TinyLog.new(dead) : TinyLog.new($stderr)
59
+ @log = log || TinyLog.new($stdout)
60
+ @dead = dead || TinyLog.new($stderr)
42
61
  @raise_on_dead = raise_on_dead
43
62
  end
44
63
 
45
64
  # 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 TinyBus usage, such as:
49
- # - .log
50
65
  def sub(topic, subber)
51
- raise TinyBus::SubscriptionToDotTopicError.new("Cannot subscribe to dot topic `#{topic}', because those are reserved for internal use") if topic.start_with?('.')
52
66
  raise TinyBus::SubscriberDoesNotMsg.new("The specified subscriber type `#{subber.class.inspect}' does not respond to #msg") unless subber.respond_to?(:msg)
53
67
 
54
68
  @subs[topic] ||= Set.new
55
69
  @subs[topic] << subber
56
70
  @stats[topic] ||= 0
71
+
72
+ msg({ '.topic' => 'sub', 'to_topic' => topic, 'subber' => subber.to.s })
57
73
  end
58
74
 
59
75
  # removes a subscriber from a topic
60
76
  def unsub(topic, subber)
61
77
  @subs[topic]&.delete(subber)
78
+
79
+ msg({ '.topic' => 'unsub', 'from_topic' => topic, 'subber' => subber.to.s })
62
80
  end
63
81
 
64
82
  # takes an incoming message and distributes it to subscribers
65
83
  #
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
84
+ # msg: the incoming message to be distributed
85
+ # lvl (optional): the logging level
69
86
  #
70
87
  # NOTE: it modifies the incoming msg object in place in order to avoid
71
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.
72
93
  def msg(msg, lvl='info')
73
- topic = msg['topic']
94
+ msg = @annotator.pipe(msg)
95
+ msg = @translator&.pipe(msg) || msg
74
96
 
75
- 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']
76
98
 
77
99
  subbers = @subs[topic]
78
100
 
79
- annotated = msg.merge!({
80
- '.time' => Time.now.utc.iso8601(6),
81
- '.msg_uuid' => SecureRandom.uuid
82
- })
83
-
84
101
  if subbers
85
102
  @stats[topic] += 1
86
- subbers.each{|s| s.msg(annotated) }
87
- @log.sent annotated
103
+ subbers.each{|s| s.msg(msg) }
104
+ @log.sent msg
88
105
  else
89
106
  if @raise_on_dead
90
107
  raise TinyBus::DeadMsgError.new("Could not deliver message to topic `#{topic}'")
91
108
  else
92
109
  @stats['.dead'] += 1
93
- @dead.dead annotated
110
+ @dead.dead msg
94
111
  end
95
112
  end
96
113
  end
@@ -106,6 +123,4 @@ class TinyBus
106
123
  end
107
124
 
108
125
  class TinyBus::DeadMsgError < RuntimeError; end
109
- class TinyBus::SubscriptionToDotTopicError < RuntimeError; end
110
126
  class TinyBus::SubscriberDoesNotMsg < RuntimeError; end
111
- 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: 1.0.4
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-18 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,