tiny_bus 1.0.4 → 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 +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,