zyre 0.1.0

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