zyre 0.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.
@@ -0,0 +1,52 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'observability'
5
+ require 'observability/instrumentation' unless defined?( Observability::Instrumentation )
6
+
7
+
8
+ # Instrumentation for the Zyre library
9
+ # Refs:
10
+ # - https://github.com/zeromq/zyre
11
+ # - https://gitlab.com/ravngroup/open-source/ruby-zyre
12
+ module Observability::Instrumentation::Zyre
13
+ extend Observability::Instrumentation
14
+
15
+ depends_on 'Zyre'
16
+
17
+
18
+ when_installed( 'Zyre::Node' ) do
19
+ Zyre::Node.extend( Observability )
20
+ Zyre::Node.observe_class_method( :new )
21
+ Zyre::Node.observe_method( :port= )
22
+ Zyre::Node.observe_method( :evasive_timeout= )
23
+ Zyre::Node.observe_method( :interface= )
24
+ Zyre::Node.observe_method( :endpoint= )
25
+ Zyre::Node.observe_method( :set_header )
26
+ Zyre::Node.observe_method( :start )
27
+ Zyre::Node.observe_method( :stop )
28
+ Zyre::Node.observe_method( :join )
29
+ Zyre::Node.observe_method( :leave )
30
+ Zyre::Node.observe_method( :recv )
31
+ Zyre::Node.observe_method( :whisper, &self.method(:observe_whisper) )
32
+ Zyre::Node.observe_method( :shout, &self.method(:observe_shout) )
33
+ end
34
+
35
+
36
+ ###############
37
+ module_function
38
+ ###############
39
+
40
+ ### Observer callback for the #whisper method.
41
+ def observe_whisper( peer_uuid, msg )
42
+ Observability.observer.add( peer_uuid: peer_uuid, message: msg )
43
+ end
44
+
45
+
46
+ ### Observer callback for the #shout method.
47
+ def observe_shout( group, msg )
48
+ Observability.observer.add( group: group, message: msg )
49
+ end
50
+
51
+ end # module Observability::Instrumentation::Zyre
52
+
@@ -0,0 +1,53 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'loggability'
5
+
6
+ require_relative 'zyre_ext'
7
+
8
+
9
+ # A binding to libzyre
10
+ module Zyre
11
+ extend Loggability
12
+
13
+
14
+ # Gem version (semver)
15
+ VERSION = '0.1.0'
16
+
17
+
18
+ # Set up a logger for Zyre classes
19
+ log_as :zyre
20
+
21
+
22
+ ### Wait on one or more +nodes+ to become readable, returning the first one that does
23
+ ### or +nil+ if the +timeout+ is zero or greater and at least that many seconds elapse.
24
+ ### Specify a +timeout+ of -1 to wait indefinitely. The timeout is in floating-point
25
+ ### seconds.
26
+ ###
27
+ ### Raises an Interrupt if the call is interrupted or the ZMQ context is destroyed.
28
+ def self::wait( *nodes, timeout: -1 )
29
+ nodes = nodes.flatten
30
+ return nil if nodes.empty?
31
+ return self.wait2( nodes, timeout )
32
+ end
33
+
34
+
35
+ ### If the given +key+ is a Symbol, transform it into an RFC822-style header key. If
36
+ ### it's not a Symbol, returns it unchanged.
37
+ def self::transform_header_key( key )
38
+ if key.is_a?( Symbol )
39
+ key = key.to_s.gsub( /_/, '-' ).capitalize
40
+ end
41
+
42
+ return key
43
+ end
44
+
45
+
46
+ ### Transform the given +headers+ hash into a form that can be passed to Zyre.
47
+ def self::normalize_headers( headers )
48
+ return headers.
49
+ transform_keys {|k| Zyre.transform_header_key(k) }.
50
+ transform_values {|v| v.to_s.encode('us-ascii') }
51
+ end
52
+
53
+ end # module Zyre
@@ -0,0 +1,82 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'loggability'
5
+
6
+ require 'zyre' unless defined?( Zyre )
7
+
8
+
9
+ #--
10
+ # See also: ext/zyre_ext/event.c
11
+ class Zyre::Event
12
+ extend Loggability
13
+
14
+
15
+ # Send logging to the 'zyre' logger
16
+ log_to :zyre
17
+
18
+ # Don't allow direct instantiation
19
+ private_class_method :new
20
+
21
+
22
+ # Autoload concrete event types
23
+ autoload :Enter, 'zyre/event/enter'
24
+ autoload :Evasive, 'zyre/event/evasive'
25
+ autoload :Exit, 'zyre/event/exit'
26
+ autoload :Join, 'zyre/event/join'
27
+ autoload :Leave, 'zyre/event/leave'
28
+ autoload :Shout, 'zyre/event/shout'
29
+ autoload :Silent, 'zyre/event/silent'
30
+ autoload :Stop, 'zyre/event/stop'
31
+ autoload :Whisper, 'zyre/event/whisper'
32
+
33
+
34
+ ### Given the +name+ of an event type, return the Zyre::Event subclass that
35
+ ### corresponds to it.
36
+ def self::type_by_name( name )
37
+ capname = name.to_s.capitalize
38
+ classobj = self.const_get( capname )
39
+ rescue NameError => err
40
+ self.log.debug( err )
41
+ return nil
42
+ end
43
+
44
+
45
+ ### Return the event type as Zyre refers to it.
46
+ def self::type_name
47
+ return self.name[ /.*::(\w+)/, 1 ].upcase
48
+ end
49
+
50
+
51
+ # Some convenience aliases
52
+ alias_method :message, :msg
53
+
54
+
55
+ ### Returns +true+ if the specified +criteria+ match attribute of the event.
56
+ def match( criteria )
57
+ return criteria.all? do |key, val|
58
+ self.respond_to?( key ) && self.public_send( key ) == val
59
+ end
60
+ end
61
+
62
+
63
+ ### Return a string describing this event, suitable for debugging.
64
+ def inspect
65
+ details = self.inspect_details
66
+ details = ' ' + details unless details.start_with?( ' ' )
67
+
68
+ return "#<%p:%#016x%s>" % [
69
+ self.class,
70
+ self.object_id,
71
+ details,
72
+ ]
73
+ end
74
+
75
+
76
+ ### Provide the details of the inspect message. Defaults to an empty string.
77
+ def inspect_details
78
+ return ''
79
+ end
80
+
81
+ end # class Zyre::Event
82
+
@@ -0,0 +1,19 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'zyre/event' unless defined?( Zyre::Event )
5
+
6
+
7
+ class Zyre::Event::Enter < Zyre::Event
8
+
9
+ ### Provide the details of the inspect message.
10
+ def inspect_details
11
+ return "%s (%s at %s) has entered the network: %p" % [
12
+ self.peer_uuid,
13
+ self.peer_name,
14
+ self.peer_addr,
15
+ self.headers,
16
+ ]
17
+ end
18
+
19
+ end # class Zyre::Event::Enter
@@ -0,0 +1,17 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'zyre/event' unless defined?( Zyre::Event )
5
+
6
+
7
+ class Zyre::Event::Evasive < Zyre::Event
8
+
9
+ ### Provide the details of the inspect message.
10
+ def inspect_details
11
+ return "%s (%s) is being evasive and will be pinged manually" % [
12
+ self.peer_uuid,
13
+ self.peer_name,
14
+ ]
15
+ end
16
+
17
+ end # class Zyre::Event::Evasive
@@ -0,0 +1,18 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'zyre/event' unless defined?( Zyre::Event )
5
+
6
+
7
+ class Zyre::Event::Exit < Zyre::Event
8
+
9
+ ### Provide the details of the inspect message.
10
+ def inspect_details
11
+ return "%s (%s) has left the network" % [
12
+ self.peer_uuid,
13
+ self.peer_name,
14
+ self.headers,
15
+ ]
16
+ end
17
+
18
+ end # class Zyre::Event::Exit
@@ -0,0 +1,18 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'zyre/event' unless defined?( Zyre::Event )
5
+
6
+
7
+ class Zyre::Event::Join < Zyre::Event
8
+
9
+ ### Provide the details of the inspect message.
10
+ def inspect_details
11
+ return "%s (%s) joined «%s»" % [
12
+ self.peer_uuid,
13
+ self.peer_name,
14
+ self.group
15
+ ]
16
+ end
17
+
18
+ end # class Zyre::Event::Join
@@ -0,0 +1,18 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'zyre/event' unless defined?( Zyre::Event )
5
+
6
+
7
+ class Zyre::Event::Leave < Zyre::Event
8
+
9
+ ### Provide the details of the inspect message.
10
+ def inspect_details
11
+ return "%s (%s) left «%s»" % [
12
+ self.peer_uuid,
13
+ self.peer_name,
14
+ self.group
15
+ ]
16
+ end
17
+
18
+ end # class Zyre::Event::Leave
@@ -0,0 +1,19 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'zyre/event' unless defined?( Zyre::Event )
5
+
6
+
7
+ class Zyre::Event::Shout < Zyre::Event
8
+
9
+ ### Provide the details of the inspect message.
10
+ def inspect_details
11
+ return "shout from %s (%s) on «%s»: %p" % [
12
+ self.peer_uuid,
13
+ self.peer_name,
14
+ self.group,
15
+ self.msg,
16
+ ]
17
+ end
18
+
19
+ end # class Zyre::Event::Shout
@@ -0,0 +1,18 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'zyre/event' unless defined?( Zyre::Event )
5
+
6
+
7
+ class Zyre::Event::Silent < Zyre::Event
8
+
9
+ ### Provide the details of the inspect message.
10
+ def inspect_details
11
+ return "%s (%s) isn't responding to pings" % [
12
+ self.peer_uuid,
13
+ self.peer_name,
14
+ self.headers,
15
+ ]
16
+ end
17
+
18
+ end # class Zyre::Event::Silent
@@ -0,0 +1,9 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'zyre/event' unless defined?( Zyre::Event )
5
+
6
+
7
+ class Zyre::Event::Stop < Zyre::Event
8
+
9
+ end # class Zyre::Event::Stop
@@ -0,0 +1,18 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'zyre/event' unless defined?( Zyre::Event )
5
+
6
+
7
+ class Zyre::Event::Whisper < Zyre::Event
8
+
9
+ ### Provide the details of the inspect message.
10
+ def inspect_details
11
+ return "whisper from %s (%s): %p" % [
12
+ self.peer_uuid,
13
+ self.peer_name,
14
+ self.msg,
15
+ ]
16
+ end
17
+
18
+ end # class Zyre::Event::Whisper
@@ -0,0 +1,147 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'loggability'
5
+
6
+ require 'zyre' unless defined?( Zyre )
7
+
8
+
9
+ #--
10
+ # See also: ext/zyre_ext/node.c
11
+ class Zyre::Node
12
+ extend Loggability
13
+
14
+
15
+ # Use the Zyre logger
16
+ log_to :zyre
17
+
18
+
19
+
20
+ ### Yield each incoming event to the block. If no block is given, returns an
21
+ ### enumerator instead.
22
+ def each_event( &block )
23
+ iter = self.make_event_enum
24
+ return iter.each( &block ) if block
25
+ return iter
26
+ end
27
+ alias_method :each, :each_event
28
+
29
+
30
+ ### Wait for an event of a given +event_type+ (e.g., :JOIN) and matching any
31
+ ### optional +criteria+, returning the event if a matching one was seen. If a
32
+ ### +timeout+ is given and the event hasn't been seen after the +timeout+
33
+ ### seconds have elapsed, return +nil+. If a block is given, call the block
34
+ ### for each (non-matching) event that arrives in the interim. Note that the
35
+ ### execution time of the block is counted in the timeout.
36
+ def wait_for( event_type, timeout: nil, **criteria, &block )
37
+ expected_type = Zyre::Event.type_by_name( event_type ) or
38
+ raise ArgumentError, "no such event type %p" % [ event_type ]
39
+
40
+ if timeout
41
+ return self.wait_for_with_timeout( expected_type, timeout, **criteria, &block )
42
+ else
43
+ return self.wait_for_indefinitely( expected_type, **criteria, &block )
44
+ end
45
+ end
46
+
47
+
48
+ ### Set headers from the given +hash+. Convenience wrapper for #set_header. Symbol
49
+ ### keys will have `_` characters converted to `-` and will be capitalized when
50
+ ### converted into Strings. E.g.,
51
+ ###
52
+ ### headers = { content_type: 'application/json' }
53
+ ###
54
+ ### will call:
55
+ ###
56
+ ### .set_header( 'Content-type', 'application/json' )
57
+ ###
58
+ def headers=( hash )
59
+ hash.each do |key, val|
60
+ key = Zyre.transform_header_key( key )
61
+ self.set_header( key.to_s, val.to_s )
62
+ end
63
+ end
64
+
65
+
66
+ ### Return a string describing the node suitable for debugging.
67
+ def inspect
68
+ return "#<%p:%#016x %s[%s]>" % [
69
+ self.class,
70
+ self.object_id,
71
+ self.name,
72
+ self.uuid,
73
+ ]
74
+ end
75
+
76
+
77
+
78
+ #########
79
+ protected
80
+ #########
81
+
82
+ ### Create an Enumerator that yields each event as it comes in. If there is
83
+ ### no event to read, block until there is one.
84
+ def make_event_enum
85
+ return Enumerator.new do |yielder|
86
+ while event = self.recv
87
+ yielder.yield( event )
88
+ end
89
+ end
90
+ end
91
+
92
+
93
+ ### Wait for an event of the given +event_class+ and +criteria+, returning it when it
94
+ ### arrives. Blocks indefinitely until it arrives or interrupted.
95
+ def wait_for_indefinitely( event_class, **criteria, &block )
96
+ poller = Zyre::Poller.new( self )
97
+ while poller.wait
98
+ event = self.recv
99
+ if event.kind_of?( event_class ) && event.match( criteria )
100
+ return event
101
+ else
102
+ block.call( event ) if block
103
+ end
104
+ end
105
+ end
106
+
107
+
108
+ ### Wait for an event of the given +event_class+ and +criteria+, returning it if
109
+ ### it arrives before +timeout+ seconds elapses. If the timeout elapses first,
110
+ ### return +nil+.
111
+ def wait_for_with_timeout( event_class, timeout, **criteria, &block )
112
+ start_time = get_monotime()
113
+ timeout_at = start_time + timeout
114
+
115
+ poller = Zyre::Poller.new( self )
116
+
117
+ timeout = timeout_at - get_monotime()
118
+ while timeout > 0
119
+ if poller.wait( timeout )
120
+ event = self.recv
121
+ if event.kind_of?( event_class ) && event.match( criteria )
122
+ return event
123
+ else
124
+ block.call( event ) if block
125
+ end
126
+ else
127
+ break
128
+ end
129
+
130
+ timeout = timeout_at - get_monotime()
131
+ end
132
+
133
+ return nil
134
+
135
+ end
136
+
137
+
138
+ #######
139
+ private
140
+ #######
141
+
142
+ ### Return the monotonic time.
143
+ def get_monotime
144
+ return Process.clock_gettime( Process::CLOCK_MONOTONIC )
145
+ end
146
+
147
+ end # class Zyre::Node