zyre 0.1.0 → 0.2.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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/History.md +7 -0
- data/README.md +69 -5
- data/ext/zyre_ext/event.c +91 -25
- data/ext/zyre_ext/node.c +22 -16
- data/ext/zyre_ext/zyre_ext.c +58 -0
- data/ext/zyre_ext/zyre_ext.h +6 -0
- data/lib/observability/instrumentation/zyre.rb +4 -4
- data/lib/zyre.rb +1 -1
- data/lib/zyre/event.rb +8 -0
- data/lib/zyre/testing.rb +12 -3
- data/spec/observability/instrumentation/zyre_spec.rb +2 -2
- data/spec/zyre/event_spec.rb +49 -0
- data/spec/zyre/node_spec.rb +75 -8
- data/spec/zyre/testing_spec.rb +36 -0
- metadata +2 -2
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bc86fa098a17006c4e470186fb0f98910f0d443ef62351f18aa2c1f8962c9bcc
|
4
|
+
data.tar.gz: aac8d6fa401e07df184f74fa0f9ef4e31e46bb91997b5af9eafe71f8048460c8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6d44fcc7a7c5b63a6e471957d5361ea9595b34b2d35f458f61cb6ee5e19db8f624e5e857203fac49992dab6160937e57571069057894ec2d8e2c51801744e88f
|
7
|
+
data.tar.gz: d5be9dc4fd921a16679850d505bb8d81ad1bcd3029ad7b1ec84277957653c345f0ebdcedf3e7a9139111a7320fccdc797f1a316ff74c2860b3b56c098ffc546f
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data.tar.gz.sig
CHANGED
Binary file
|
data/History.md
CHANGED
@@ -1,6 +1,13 @@
|
|
1
1
|
# Release History for zyre
|
2
2
|
|
3
3
|
---
|
4
|
+
## v0.2.0 [2020-11-09] Michael Granger <ged@faeriemud.org>
|
5
|
+
|
6
|
+
Improvements:
|
7
|
+
|
8
|
+
- Add multipart messaging.
|
9
|
+
- Allow positional args for testing factory SHOUTs and WHISPERs
|
10
|
+
|
4
11
|
|
5
12
|
## v0.1.0 [2020-11-05] Michael Granger <ged@faeriemud.org>
|
6
13
|
|
data/README.md
CHANGED
@@ -21,20 +21,84 @@ reliable group messaging over local area networks, an implementation of [the Zer
|
|
21
21
|
|
22
22
|
### Examples
|
23
23
|
|
24
|
-
|
25
|
-
|
24
|
+
Zyre is a P2P library which has two modes: pub/sub and direct messaging. To use it, you create a node, optionally join some groups (subscribing), and start it. Then you can send broadcast messages to all other nodes in a group, or direct messages to a particular node.
|
25
|
+
|
26
|
+
This example joins the Zyre network and dumps messages it sees in the 'global' group to stderr:
|
27
|
+
|
26
28
|
node = Zyre::Node.new
|
27
|
-
node.
|
29
|
+
node.join( 'global' )
|
28
30
|
node.start
|
29
31
|
|
30
|
-
|
32
|
+
node.each_event do |event|
|
31
33
|
event.print
|
32
34
|
end
|
33
35
|
|
36
|
+
To send a direct message to a different node you need to know its `UUID`. There are number of ways to discover this:
|
37
|
+
|
38
|
+
# The UUIDs of all connected peers
|
39
|
+
node.peers
|
40
|
+
# The UUIDs of all the peers which have joined the `general` group
|
41
|
+
node.peers_by_group( 'general' )
|
42
|
+
# The UUID of the peer that sent the event
|
43
|
+
received_event.peer_uuid
|
44
|
+
|
45
|
+
You read events from the network with the Zyre::Node#recv method, but it blocks until an event arrives:
|
46
|
+
|
47
|
+
event = node.recv
|
48
|
+
|
49
|
+
You can also iterate over arriving events:
|
50
|
+
|
51
|
+
node.each do |event|
|
52
|
+
...
|
53
|
+
end
|
54
|
+
|
55
|
+
or use Zyre::Node#each as an Enumerator:
|
56
|
+
|
57
|
+
five_events = node.each_event.take( 5 )
|
58
|
+
|
59
|
+
You can also wait for a certain type of event:
|
60
|
+
|
61
|
+
event = node.wait_for( :SHOUT )
|
62
|
+
|
63
|
+
and time out if it doesn't arrive within a certain length of time:
|
64
|
+
|
65
|
+
event = node.wait_for( :ENTER, timeout: 2.0 ) or
|
66
|
+
raise "No one else on the network!"
|
67
|
+
|
68
|
+
You can join a group with Zyre::Node#join:
|
69
|
+
|
70
|
+
node.join( 'alerts' )
|
71
|
+
|
72
|
+
and you can detect other nodes joining one of your groups by looking for JOIN events (Zyre::Event::Join objects):
|
73
|
+
|
74
|
+
node.wait_for( :JOIN )
|
75
|
+
|
76
|
+
You can publish a message to all nodes in a group with Zyre::Node#shout:
|
77
|
+
|
78
|
+
node.shout( "group1", "This is a message." )
|
79
|
+
|
80
|
+
and read it:
|
81
|
+
|
82
|
+
event = other_node.recv
|
83
|
+
event.is_multipart? # => false
|
84
|
+
event.msg # => "This is a message."
|
85
|
+
event.multipart_msg # => ["This is a message."]
|
86
|
+
|
87
|
+
|
88
|
+
Or publish a message with multiple parts:
|
89
|
+
|
90
|
+
node.shout( "group1", 'message.type', "This is a message." )
|
91
|
+
|
92
|
+
and read it:
|
93
|
+
|
94
|
+
event = other_node.recv
|
95
|
+
event.is_multipart? # => true
|
96
|
+
event.msg # => "message.type"
|
97
|
+
event.multipart_msg # => ["message.type", "This is a message."]
|
98
|
+
|
34
99
|
|
35
100
|
### To-Do
|
36
101
|
|
37
|
-
* Implement Zyre::Node#peer_groups and Zyre::Node#peer_address
|
38
102
|
* Implement the draft API methods on Zyre::Node
|
39
103
|
* Hook up logging via `zsys_set_logsender`
|
40
104
|
* Add richer matching to Zyre::Event#match.
|
data/ext/zyre_ext/event.c
CHANGED
@@ -121,6 +121,7 @@ rzyre_copy_string( VALUE string )
|
|
121
121
|
const char *c_string = StringValueCStr( string );
|
122
122
|
char *copy = (char *) zmalloc( strnlen(c_string, BUFSIZ) + 1 );
|
123
123
|
|
124
|
+
rzyre_log( "debug", "Copying string `%s`.", c_string );
|
124
125
|
assert( copy );
|
125
126
|
stpncpy( copy, c_string, strnlen(c_string, BUFSIZ) + 1 );
|
126
127
|
|
@@ -134,6 +135,7 @@ rzyre_copy_required_string( VALUE string, const char *field_name )
|
|
134
135
|
if ( RB_TYPE_P(string, T_UNDEF) ) {
|
135
136
|
rb_raise( rb_eArgError, "missing required field :%s", field_name );
|
136
137
|
} else {
|
138
|
+
rzyre_log( "debug", "Required field %s is defined.", field_name );
|
137
139
|
return rzyre_copy_string( string );
|
138
140
|
}
|
139
141
|
}
|
@@ -182,27 +184,37 @@ static VALUE
|
|
182
184
|
rzyre_event_s_synthesize( int argc, VALUE *argv, VALUE klass )
|
183
185
|
{
|
184
186
|
VALUE rval, event_type, peer_uuid, kwargs, event_class;
|
185
|
-
|
187
|
+
VALUE kwvals[5] = { Qundef, Qundef, Qundef, Qundef, Qundef };
|
186
188
|
static ID keyword_ids[5];
|
187
189
|
zyre_event_t *ptr = NULL;
|
188
190
|
|
189
191
|
// Parse the arguments + keyword arguments
|
190
192
|
if ( !keyword_ids[0] ) {
|
191
|
-
CONST_ID( keyword_ids[0], "peer_name");
|
192
|
-
CONST_ID( keyword_ids[1], "headers");
|
193
|
-
CONST_ID( keyword_ids[2], "peer_addr");
|
194
|
-
CONST_ID( keyword_ids[3], "group");
|
195
|
-
CONST_ID( keyword_ids[4], "msg");
|
193
|
+
CONST_ID( keyword_ids[0], "peer_name" );
|
194
|
+
CONST_ID( keyword_ids[1], "headers" );
|
195
|
+
CONST_ID( keyword_ids[2], "peer_addr" );
|
196
|
+
CONST_ID( keyword_ids[3], "group" );
|
197
|
+
CONST_ID( keyword_ids[4], "msg" );
|
196
198
|
}
|
197
199
|
|
200
|
+
rzyre_log( "debug", "Scanning %d synthesize args.", argc );
|
198
201
|
rb_scan_args( argc, argv, "2:", &event_type, &peer_uuid, &kwargs );
|
199
202
|
if ( RTEST(kwargs) ) {
|
203
|
+
rzyre_log( "debug", " scanning keyword args: %s", RSTRING_PTR(rb_inspect(kwargs)) );
|
200
204
|
rb_get_kwargs( kwargs, keyword_ids, 0, 5, kwvals );
|
201
205
|
}
|
202
206
|
|
203
207
|
// Translate the event type argument into the appropriate class and instantiate it
|
208
|
+
rzyre_log( "debug", "Creating an instance of a %s event.", RSTRING_PTR(rb_inspect(event_type )) );
|
204
209
|
event_class = rb_funcall( klass, rb_intern("type_by_name"), 1, event_type );
|
205
|
-
|
210
|
+
|
211
|
+
if ( RTEST(event_class) ) {
|
212
|
+
event_type = rb_funcall( event_class, rb_intern("type_name"), 0 );
|
213
|
+
} else {
|
214
|
+
rb_raise( rb_eArgError, "don't know how to create %s events",
|
215
|
+
RSTRING_PTR(rb_inspect(event_type)) );
|
216
|
+
}
|
217
|
+
|
206
218
|
rval = rb_class_new_instance( 0, NULL, event_class );
|
207
219
|
|
208
220
|
// Set up the zyre_event memory for the object
|
@@ -213,6 +225,7 @@ rzyre_event_s_synthesize( int argc, VALUE *argv, VALUE klass )
|
|
213
225
|
ptr->peer_uuid = rzyre_copy_string( peer_uuid );
|
214
226
|
|
215
227
|
// Set the peer_name or default it if it wasn't specified
|
228
|
+
rzyre_log( "debug", "Starting type-specific setup." );
|
216
229
|
if ( !RB_TYPE_P(kwvals[0], T_UNDEF) ) {
|
217
230
|
ptr->peer_name = rzyre_copy_string( kwvals[0] );
|
218
231
|
} else {
|
@@ -234,27 +247,25 @@ rzyre_event_s_synthesize( int argc, VALUE *argv, VALUE klass )
|
|
234
247
|
ptr->group = rzyre_copy_required_string( kwvals[3], "group" );
|
235
248
|
}
|
236
249
|
else if ( streq(ptr->type, "WHISPER") ) {
|
237
|
-
|
238
|
-
|
250
|
+
if ( RB_TYPE_P(kwvals[4], T_UNDEF) )
|
251
|
+
rb_raise( rb_eArgError, "missing required field :msg" );
|
239
252
|
|
240
|
-
|
241
|
-
|
242
|
-
msg =
|
253
|
+
rzyre_log( "debug", "Making a WHISPER zmsg from the :msg value: %s",
|
254
|
+
RSTRING_PTR(rb_inspect(kwvals[4])) );
|
255
|
+
ptr->msg = rzyre_make_zmsg_from( kwvals[4] );
|
243
256
|
}
|
244
257
|
else if ( streq(ptr->type, "SHOUT") ) {
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
zmsg_addstr( msg, msg_str );
|
258
|
+
if ( RB_TYPE_P(kwvals[4], T_UNDEF) )
|
259
|
+
rb_raise( rb_eArgError, "missing required field :msg" );
|
249
260
|
|
261
|
+
rzyre_log( "debug", "Making a SHOUT zmsg from the :msg value: %s",
|
262
|
+
RSTRING_PTR(rb_inspect(kwvals[4])) );
|
250
263
|
ptr->group = rzyre_copy_required_string( kwvals[3], "group" );
|
251
|
-
ptr->msg =
|
252
|
-
msg = NULL;
|
253
|
-
}
|
254
|
-
else if ( streq(ptr->type, "LEADER") ) {
|
255
|
-
ptr->group = rzyre_copy_required_string( kwvals[3], "group" );
|
264
|
+
ptr->msg = rzyre_make_zmsg_from( kwvals[4] );
|
256
265
|
}
|
257
266
|
|
267
|
+
rzyre_log_obj( rval, "debug", "Synthesized a %s event.", ptr->type );
|
268
|
+
|
258
269
|
return rval;
|
259
270
|
}
|
260
271
|
|
@@ -397,9 +408,32 @@ rzyre_event_group( VALUE self ) {
|
|
397
408
|
|
398
409
|
/*
|
399
410
|
* call-seq:
|
400
|
-
* event.
|
411
|
+
* event.msg_size -> int
|
412
|
+
*
|
413
|
+
* Return the number of frames present in the event's message (if it has one). Returns
|
414
|
+
* nil if there is no message.
|
415
|
+
*
|
416
|
+
*/
|
417
|
+
static VALUE
|
418
|
+
rzyre_event_msg_size( VALUE self )
|
419
|
+
{
|
420
|
+
zyre_event_t *ptr = rzyre_get_event( self );
|
421
|
+
zmsg_t *msg = zyre_event_msg( ptr );
|
422
|
+
VALUE rval = Qnil;
|
423
|
+
|
424
|
+
if ( msg ) {
|
425
|
+
rval = INT2FIX( zmsg_size(msg) );
|
426
|
+
}
|
427
|
+
|
428
|
+
return rval;
|
429
|
+
}
|
430
|
+
|
431
|
+
|
432
|
+
/*
|
433
|
+
* call-seq:
|
434
|
+
* event.msg
|
401
435
|
*
|
402
|
-
* Returns the
|
436
|
+
* Returns the data from the first frame of the message from the receiver.
|
403
437
|
*/
|
404
438
|
static VALUE
|
405
439
|
rzyre_event_msg( VALUE self ) {
|
@@ -407,12 +441,11 @@ rzyre_event_msg( VALUE self ) {
|
|
407
441
|
zmsg_t *msg = zyre_event_msg( ptr );
|
408
442
|
VALUE rval = Qnil;
|
409
443
|
|
410
|
-
// :TODO: Support multipart messages when Zyre does.
|
411
444
|
if ( msg ) {
|
412
445
|
zframe_t *frame = zmsg_first( msg );
|
413
446
|
char *str = zframe_strdup( frame );
|
414
447
|
|
415
|
-
rval =
|
448
|
+
rval = rb_enc_str_new( str, zframe_size(frame), rb_ascii8bit_encoding() );
|
416
449
|
rb_obj_freeze( rval );
|
417
450
|
|
418
451
|
free( str );
|
@@ -422,6 +455,37 @@ rzyre_event_msg( VALUE self ) {
|
|
422
455
|
}
|
423
456
|
|
424
457
|
|
458
|
+
/*
|
459
|
+
* call-seq:
|
460
|
+
* event.multipart_msg
|
461
|
+
*
|
462
|
+
* Returns the data from every frame of the message from the receiver.
|
463
|
+
*/
|
464
|
+
static VALUE
|
465
|
+
rzyre_event_multipart_msg( VALUE self ) {
|
466
|
+
zyre_event_t *ptr = rzyre_get_event( self );
|
467
|
+
zmsg_t *msg = zyre_event_msg( ptr );
|
468
|
+
VALUE rval = rb_ary_new();
|
469
|
+
|
470
|
+
if ( msg ) {
|
471
|
+
zframe_t *frame = zmsg_first( msg );
|
472
|
+
|
473
|
+
while ( frame ) {
|
474
|
+
char *str = zframe_strdup( frame );
|
475
|
+
VALUE data = rb_enc_str_new( str, zframe_size(frame), rb_ascii8bit_encoding() );
|
476
|
+
|
477
|
+
rb_obj_freeze( data );
|
478
|
+
rb_ary_push( rval, data );
|
479
|
+
|
480
|
+
free( str );
|
481
|
+
frame = zmsg_next( msg );
|
482
|
+
}
|
483
|
+
}
|
484
|
+
|
485
|
+
return rval;
|
486
|
+
}
|
487
|
+
|
488
|
+
|
425
489
|
/*
|
426
490
|
* call-seq:
|
427
491
|
* event.print
|
@@ -471,7 +535,9 @@ rzyre_init_event( void ) {
|
|
471
535
|
rb_define_method( rzyre_cZyreEvent, "headers", rzyre_event_headers, 0 );
|
472
536
|
rb_define_method( rzyre_cZyreEvent, "header", rzyre_event_header, 1 );
|
473
537
|
rb_define_method( rzyre_cZyreEvent, "group", rzyre_event_group, 0 );
|
538
|
+
rb_define_method( rzyre_cZyreEvent, "msg_size", rzyre_event_msg_size, 0 );
|
474
539
|
rb_define_method( rzyre_cZyreEvent, "msg", rzyre_event_msg, 0 );
|
540
|
+
rb_define_method( rzyre_cZyreEvent, "multipart_msg", rzyre_event_multipart_msg, 0 );
|
475
541
|
rb_define_method( rzyre_cZyreEvent, "print", rzyre_event_print, 0 );
|
476
542
|
|
477
543
|
rb_require( "zyre/event" );
|
data/ext/zyre_ext/node.c
CHANGED
@@ -526,23 +526,26 @@ rzyre_node_recv( VALUE self )
|
|
526
526
|
|
527
527
|
/*
|
528
528
|
* call-seq:
|
529
|
-
* node.whisper( peer_uuid,
|
529
|
+
* node.whisper( peer_uuid, *messages ) -> int
|
530
530
|
*
|
531
531
|
* Send a +message+ to a single +peer+ specified as a UUID string.
|
532
532
|
*
|
533
533
|
*/
|
534
534
|
static VALUE
|
535
|
-
rzyre_node_whisper(
|
535
|
+
rzyre_node_whisper( int argc, VALUE *argv, VALUE self )
|
536
536
|
{
|
537
537
|
zyre_t *ptr = rzyre_get_node( self );
|
538
|
-
|
539
|
-
|
538
|
+
VALUE peer_uuid, msg_parts;
|
539
|
+
char *peer_uuid_str;
|
540
|
+
zmsg_t *msg;
|
540
541
|
int rval;
|
541
542
|
|
542
|
-
|
543
|
-
assert( msg_str );
|
543
|
+
rb_scan_args( argc, argv, "1*", &peer_uuid, &msg_parts );
|
544
544
|
|
545
|
-
|
545
|
+
peer_uuid_str = StringValueCStr( peer_uuid );
|
546
|
+
msg = rzyre_make_zmsg_from( msg_parts );
|
547
|
+
|
548
|
+
rval = zyre_whisper( ptr, peer_uuid_str, &msg );
|
546
549
|
|
547
550
|
return rval ? Qtrue : Qfalse;
|
548
551
|
}
|
@@ -550,23 +553,26 @@ rzyre_node_whisper( VALUE self, VALUE peer_uuid, VALUE msg )
|
|
550
553
|
|
551
554
|
/*
|
552
555
|
* call-seq:
|
553
|
-
* node.shout( group,
|
556
|
+
* node.shout( group, *messages ) -> int
|
554
557
|
*
|
555
558
|
* Send +message+ to a named +group+.
|
556
559
|
*
|
557
560
|
*/
|
558
561
|
static VALUE
|
559
|
-
rzyre_node_shout(
|
562
|
+
rzyre_node_shout( int argc, VALUE *argv, VALUE self )
|
560
563
|
{
|
561
564
|
zyre_t *ptr = rzyre_get_node( self );
|
562
|
-
|
563
|
-
|
565
|
+
VALUE group, msg_parts;
|
566
|
+
char *group_str;
|
567
|
+
zmsg_t *msg;
|
564
568
|
int rval;
|
565
569
|
|
566
|
-
|
567
|
-
|
570
|
+
rb_scan_args( argc, argv, "1*", &group, &msg_parts );
|
571
|
+
|
572
|
+
group_str = StringValueCStr( group );
|
573
|
+
msg = rzyre_make_zmsg_from( msg_parts );
|
568
574
|
|
569
|
-
rval =
|
575
|
+
rval = zyre_shout( ptr, group_str, &msg );
|
570
576
|
|
571
577
|
return rval ? Qtrue : Qfalse;
|
572
578
|
}
|
@@ -819,8 +825,8 @@ rzyre_init_node( void )
|
|
819
825
|
|
820
826
|
rb_define_method( rzyre_cZyreNode, "recv", rzyre_node_recv, 0 );
|
821
827
|
|
822
|
-
rb_define_method( rzyre_cZyreNode, "whisper", rzyre_node_whisper,
|
823
|
-
rb_define_method( rzyre_cZyreNode, "shout", rzyre_node_shout,
|
828
|
+
rb_define_method( rzyre_cZyreNode, "whisper", rzyre_node_whisper, -1 );
|
829
|
+
rb_define_method( rzyre_cZyreNode, "shout", rzyre_node_shout, -1 );
|
824
830
|
|
825
831
|
rb_define_method( rzyre_cZyreNode, "peers", rzyre_node_peers, 0 );
|
826
832
|
rb_define_method( rzyre_cZyreNode, "peers_by_group", rzyre_node_peers_by_group, 1 );
|
data/ext/zyre_ext/zyre_ext.c
CHANGED
@@ -68,6 +68,64 @@ rzyre_log( const char *level, const char *fmt, va_dcl )
|
|
68
68
|
}
|
69
69
|
|
70
70
|
|
71
|
+
/* --------------------------------------------------------------
|
72
|
+
* Utility functions
|
73
|
+
* -------------------------------------------------------------- */
|
74
|
+
|
75
|
+
// Struct for passing arguments through rb_protect to rzyre_add_frames_to_zmsg()
|
76
|
+
struct add_frames_to_zmsg_call {
|
77
|
+
zmsg_t *msg;
|
78
|
+
VALUE msg_parts;
|
79
|
+
};
|
80
|
+
typedef struct add_frames_to_zmsg_call add_frames_to_zmsg_call_t;
|
81
|
+
|
82
|
+
|
83
|
+
/*
|
84
|
+
* Add a frame for each object in an Array.
|
85
|
+
*/
|
86
|
+
static VALUE
|
87
|
+
rzyre_add_frames_to_zmsg( VALUE call )
|
88
|
+
{
|
89
|
+
add_frames_to_zmsg_call_t *call_ptr = (add_frames_to_zmsg_call_t *)call;
|
90
|
+
VALUE msg_part;
|
91
|
+
|
92
|
+
for ( long i = 0 ; i < RARRAY_LEN(call_ptr->msg_parts) ; i++ ) {
|
93
|
+
msg_part = rb_ary_entry( call_ptr->msg_parts, i );
|
94
|
+
zmsg_addstr( call_ptr->msg, StringValueCStr(msg_part) );
|
95
|
+
}
|
96
|
+
|
97
|
+
return Qtrue;
|
98
|
+
}
|
99
|
+
|
100
|
+
|
101
|
+
/*
|
102
|
+
* Make and return a zmsg, with one frame per object in +messages+. Caller owns the returned
|
103
|
+
* zmsg. Can raise a TypeError if one of the +messages+ can't be stringified.
|
104
|
+
*/
|
105
|
+
zmsg_t *
|
106
|
+
rzyre_make_zmsg_from( VALUE messages )
|
107
|
+
{
|
108
|
+
VALUE msgarray = rb_Array( messages );
|
109
|
+
zmsg_t *msg = zmsg_new();
|
110
|
+
static add_frames_to_zmsg_call_t call;
|
111
|
+
int state;
|
112
|
+
|
113
|
+
call.msg = msg;
|
114
|
+
call.msg_parts = msgarray;
|
115
|
+
|
116
|
+
rb_protect( rzyre_add_frames_to_zmsg, (VALUE)&call, &state );
|
117
|
+
|
118
|
+
if ( state ) {
|
119
|
+
zmsg_destroy( &msg );
|
120
|
+
rb_jump_tag( state );
|
121
|
+
}
|
122
|
+
|
123
|
+
return msg;
|
124
|
+
}
|
125
|
+
|
126
|
+
|
127
|
+
|
128
|
+
|
71
129
|
|
72
130
|
/* --------------------------------------------------------------
|
73
131
|
* Module methods
|
data/ext/zyre_ext/zyre_ext.h
CHANGED
@@ -13,6 +13,7 @@
|
|
13
13
|
#include <ruby.h>
|
14
14
|
#include <ruby/intern.h>
|
15
15
|
#include <ruby/thread.h>
|
16
|
+
#include <ruby/encoding.h>
|
16
17
|
|
17
18
|
#include "zyre.h"
|
18
19
|
#include "czmq.h"
|
@@ -83,6 +84,11 @@ extern VALUE rzyre_cZyrePoller;
|
|
83
84
|
#define IsZyreEvent( obj ) rb_obj_is_kind_of( (obj), rzyre_cZyreEvent )
|
84
85
|
#define IsZyrePoller( obj ) rb_obj_is_kind_of( (obj), rzyre_cZyrePoller )
|
85
86
|
|
87
|
+
/* --------------------------------------------------------------
|
88
|
+
* Utility functions
|
89
|
+
* -------------------------------------------------------------- */
|
90
|
+
extern zmsg_t * rzyre_make_zmsg_from _(( VALUE ));
|
91
|
+
|
86
92
|
|
87
93
|
/* -------------------------------------------------------
|
88
94
|
* Initializer functions
|
@@ -38,14 +38,14 @@ module Observability::Instrumentation::Zyre
|
|
38
38
|
###############
|
39
39
|
|
40
40
|
### Observer callback for the #whisper method.
|
41
|
-
def observe_whisper( peer_uuid,
|
42
|
-
Observability.observer.add( peer_uuid: peer_uuid,
|
41
|
+
def observe_whisper( peer_uuid, *msgs )
|
42
|
+
Observability.observer.add( peer_uuid: peer_uuid, messages: msgs )
|
43
43
|
end
|
44
44
|
|
45
45
|
|
46
46
|
### Observer callback for the #shout method.
|
47
|
-
def observe_shout( group,
|
48
|
-
Observability.observer.add( group: group,
|
47
|
+
def observe_shout( group, *msgs )
|
48
|
+
Observability.observer.add( group: group, messages: msgs )
|
49
49
|
end
|
50
50
|
|
51
51
|
end # module Observability::Instrumentation::Zyre
|
data/lib/zyre.rb
CHANGED
data/lib/zyre/event.rb
CHANGED
@@ -60,6 +60,14 @@ class Zyre::Event
|
|
60
60
|
end
|
61
61
|
|
62
62
|
|
63
|
+
### Returns +true+ if the receiving event has a multipart message.
|
64
|
+
def multipart?
|
65
|
+
size = self.msg_size
|
66
|
+
return size && size > 1
|
67
|
+
end
|
68
|
+
alias_method :is_multipart?, :multipart?
|
69
|
+
|
70
|
+
|
63
71
|
### Return a string describing this event, suitable for debugging.
|
64
72
|
def inspect
|
65
73
|
details = self.inspect_details
|
data/lib/zyre/testing.rb
CHANGED
@@ -154,8 +154,12 @@ module Zyre::Testing
|
|
154
154
|
|
155
155
|
|
156
156
|
### Generate a SHOUT event.
|
157
|
-
def shout( **overrides )
|
157
|
+
def shout( group=nil, *msg, **overrides )
|
158
158
|
uuid = overrides.delete( :peer_uuid ) || self.peer_uuid
|
159
|
+
|
160
|
+
overrides[:group] = group if group
|
161
|
+
overrides[:msg] = msg if !msg.empty?
|
162
|
+
|
159
163
|
config = {
|
160
164
|
peer_name: self.peer_name,
|
161
165
|
group: self.group,
|
@@ -166,9 +170,14 @@ module Zyre::Testing
|
|
166
170
|
end
|
167
171
|
|
168
172
|
|
169
|
-
### Generate a WHISPER event.
|
170
|
-
|
173
|
+
### Generate a WHISPER event. The first positional argument, which would
|
174
|
+
### normally be the UUID of the peer to WHISPER to is ignored, since the
|
175
|
+
### generated event's +peer_uuid+ is the sending node's not the receiving one's.
|
176
|
+
def whisper( _ignored=nil, *msg, **overrides )
|
171
177
|
uuid = overrides.delete( :peer_uuid ) || self.peer_uuid
|
178
|
+
|
179
|
+
overrides[:msg] = msg if !msg.empty?
|
180
|
+
|
172
181
|
config = {
|
173
182
|
peer_name: self.peer_name,
|
174
183
|
msg: self.msg
|
@@ -31,7 +31,7 @@ RSpec.describe 'Observability::Instrumentation::Zyre', :observability do
|
|
31
31
|
events = Observability.observer.sender.find_events( 'zyre.node.whisper' )
|
32
32
|
expect( events.length ).to eq( 1 )
|
33
33
|
expect( events.first[:peer_uuid] ).to eq( node2.uuid )
|
34
|
-
expect( events.first[:
|
34
|
+
expect( events.first[:messages] ).to eq( ['a peer-to-peer message'] )
|
35
35
|
|
36
36
|
end
|
37
37
|
|
@@ -49,7 +49,7 @@ RSpec.describe 'Observability::Instrumentation::Zyre', :observability do
|
|
49
49
|
events = Observability.observer.sender.find_events( 'zyre.node.shout' )
|
50
50
|
expect( events.length ).to eq( 1 )
|
51
51
|
expect( events.first[:group] ).to eq( 'observer-testing' )
|
52
|
-
expect( events.first[:
|
52
|
+
expect( events.first[:messages] ).to eq( ['a peer-to-peer message'] )
|
53
53
|
end
|
54
54
|
|
55
55
|
end
|
data/spec/zyre/event_spec.rb
CHANGED
@@ -135,6 +135,55 @@ RSpec.describe( Zyre::Event ) do
|
|
135
135
|
expect( result.peer_name ).to eq( 'S-' + peer_uuid[0, 6] )
|
136
136
|
end
|
137
137
|
|
138
|
+
|
139
|
+
it "raises when creating a WHISPER with no msg" do
|
140
|
+
expect {
|
141
|
+
described_class.synthesize( :WHISPER, peer_uuid )
|
142
|
+
}.to raise_error( ArgumentError, /missing required field :msg/i )
|
143
|
+
end
|
144
|
+
|
145
|
+
|
146
|
+
it "raises when creating a SHOUT with no msg" do
|
147
|
+
expect {
|
148
|
+
described_class.synthesize( :SHOUT, peer_uuid, group: 'agroup' )
|
149
|
+
}.to raise_error( ArgumentError, /missing required field :msg/i )
|
150
|
+
end
|
151
|
+
|
152
|
+
|
153
|
+
it "raises when creating a SHOUT with no group" do
|
154
|
+
expect {
|
155
|
+
described_class.synthesize( :SHOUT, peer_uuid, msg: 'amsg' )
|
156
|
+
}.to raise_error( ArgumentError, /missing required field :group/i )
|
157
|
+
end
|
158
|
+
|
159
|
+
|
160
|
+
it "raises when creating an ENTER with no peer_addr" do
|
161
|
+
expect {
|
162
|
+
described_class.synthesize( :ENTER, peer_uuid )
|
163
|
+
}.to raise_error( ArgumentError, /missing required field :peer_addr/i )
|
164
|
+
end
|
165
|
+
|
166
|
+
|
167
|
+
it "raises when creating a JOIN with no group" do
|
168
|
+
expect {
|
169
|
+
described_class.synthesize( :JOIN, peer_uuid )
|
170
|
+
}.to raise_error( ArgumentError, /missing required field :group/i )
|
171
|
+
end
|
172
|
+
|
173
|
+
|
174
|
+
it "raises when creating a LEAVE with no group" do
|
175
|
+
expect {
|
176
|
+
described_class.synthesize( :LEAVE, peer_uuid )
|
177
|
+
}.to raise_error( ArgumentError, /missing required field :group/i )
|
178
|
+
end
|
179
|
+
|
180
|
+
|
181
|
+
it "raises when creating an unknown type of event" do
|
182
|
+
expect {
|
183
|
+
described_class.synthesize( :BACKUP, peer_uuid )
|
184
|
+
}.to raise_error( ArgumentError, /don't know how to create :BACKUP events/i )
|
185
|
+
end
|
186
|
+
|
138
187
|
end
|
139
188
|
|
140
189
|
end
|
data/spec/zyre/node_spec.rb
CHANGED
@@ -162,13 +162,30 @@ RSpec.describe( Zyre::Node ) do
|
|
162
162
|
node1.wait_for( :ENTER, peer_uuid: node2.uuid )
|
163
163
|
node1.whisper( node2.uuid, TEST_WHISPER )
|
164
164
|
|
165
|
-
ev = node2.
|
166
|
-
expect( ev
|
165
|
+
ev = node2.wait_for( :WHISPER )
|
166
|
+
expect( ev ).to be_a( Zyre::Event::Whisper )
|
167
|
+
expect( ev ).to_not be_multipart
|
168
|
+
msg = ev.msg
|
169
|
+
expect( msg.encoding ).to eq( Encoding::ASCII_8BIT )
|
170
|
+
msg = msg.dup.force_encoding( 'utf-8' )
|
171
|
+
expect( msg ).to eq( TEST_WHISPER )
|
172
|
+
expect( ev.multipart_msg ).to eq( [TEST_WHISPER.b] )
|
173
|
+
end
|
167
174
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
175
|
+
|
176
|
+
it "can whisper a multipart message to another node" do
|
177
|
+
node1 = started_node()
|
178
|
+
node2 = started_node()
|
179
|
+
|
180
|
+
node1.wait_for( :ENTER, peer_uuid: node2.uuid )
|
181
|
+
node1.whisper( node2.uuid, 'poetry.snippet', TEST_WHISPER )
|
182
|
+
|
183
|
+
ev = node2.wait_for( :WHISPER, peer_uuid: node1.uuid )
|
184
|
+
expect( ev ).to be_a( Zyre::Event::Whisper )
|
185
|
+
expect( ev ).to be_multipart
|
186
|
+
expect( ev.msg.encoding ).to eq( Encoding::ASCII_8BIT )
|
187
|
+
expect( ev.msg ).to eq( 'poetry.snippet' )
|
188
|
+
expect( ev.multipart_msg ).to eq( ['poetry.snippet', TEST_WHISPER] )
|
172
189
|
end
|
173
190
|
|
174
191
|
|
@@ -190,8 +207,42 @@ RSpec.describe( Zyre::Node ) do
|
|
190
207
|
|
191
208
|
ev = node2.recv
|
192
209
|
expect( ev ).to be_a( Zyre::Event::Shout )
|
193
|
-
expect( ev
|
194
|
-
expect( ev.msg ).to eq(
|
210
|
+
expect( ev ).to_not be_multipart
|
211
|
+
expect( ev.msg.encoding ).to eq( Encoding::ASCII_8BIT )
|
212
|
+
expect( ev.msg ).to eq( TEST_SHOUT.b )
|
213
|
+
expect( ev.multipart_msg ).to eq( [TEST_SHOUT.b] )
|
214
|
+
end
|
215
|
+
|
216
|
+
|
217
|
+
it "can shout a multipart message to a group of nodes" do
|
218
|
+
node1 = started_node()
|
219
|
+
node2 = started_node()
|
220
|
+
node3 = started_node()
|
221
|
+
|
222
|
+
node1.join( 'ROOFTOP' )
|
223
|
+
node2.join( 'ROOFTOP' )
|
224
|
+
node3.join( 'ROOFTOP' )
|
225
|
+
|
226
|
+
2.times do
|
227
|
+
node1.wait_for( :JOIN, group: 'ROOFTOP' )
|
228
|
+
end
|
229
|
+
node1.shout( 'ROOFTOP', "random.poetry", TEST_SHOUT )
|
230
|
+
|
231
|
+
ev = node2.wait_for( :SHOUT, group: 'ROOFTOP' )
|
232
|
+
expect( ev ).to be_a( Zyre::Event::Shout )
|
233
|
+
expect( ev ).to be_multipart
|
234
|
+
expect( ev.msg ).to eq( 'random.poetry'.b )
|
235
|
+
expect( ev.multipart_msg ).to eq( ['random.poetry'.b, TEST_SHOUT.b] )
|
236
|
+
end
|
237
|
+
|
238
|
+
|
239
|
+
it "handles unstringifiable messages gracefully" do
|
240
|
+
node1 = started_node()
|
241
|
+
node1.join( 'ROOFTOP' )
|
242
|
+
|
243
|
+
expect {
|
244
|
+
node1.shout( 'ROOFTOP', nil )
|
245
|
+
}.to raise_error( TypeError, /nil/i )
|
195
246
|
end
|
196
247
|
|
197
248
|
|
@@ -236,6 +287,22 @@ RSpec.describe( Zyre::Node ) do
|
|
236
287
|
end
|
237
288
|
|
238
289
|
|
290
|
+
it "knows what the headers of its peers are" do
|
291
|
+
node1 = started_node()
|
292
|
+
node2 = started_node() do |node|
|
293
|
+
node.headers = {
|
294
|
+
protocol_version: 2,
|
295
|
+
content_type: 'application/javascript',
|
296
|
+
start_time: Time.now.to_f
|
297
|
+
}
|
298
|
+
end
|
299
|
+
|
300
|
+
node1.wait_for( :enter, peer_uuid: node2.uuid )
|
301
|
+
|
302
|
+
expect( node1.peer_header_value(node2.uuid, 'Protocol-version') ).to eq( '2' )
|
303
|
+
end
|
304
|
+
|
305
|
+
|
239
306
|
it "has a blocking iterator" do
|
240
307
|
node = described_class.new
|
241
308
|
|
data/spec/zyre/testing_spec.rb
CHANGED
@@ -181,6 +181,20 @@ RSpec.describe( Zyre::Testing ) do
|
|
181
181
|
end
|
182
182
|
|
183
183
|
|
184
|
+
it "can override SHOUT event message and group using positional parameters" do
|
185
|
+
event = factory.shout( 'control', 'data1', 'data2' )
|
186
|
+
|
187
|
+
expect( event ).to be_a( Zyre::Event::Shout )
|
188
|
+
expect( event.peer_uuid ).to eq( factory.peer_uuid )
|
189
|
+
expect( event.peer_name ).to eq( 'lancer-6' )
|
190
|
+
expect( event.headers ).to be_empty
|
191
|
+
expect( event.msg ).to eq( 'data1' )
|
192
|
+
expect( event ).to be_multipart
|
193
|
+
expect( event.multipart_msg ).to eq( ['data1'.b, 'data2'.b] )
|
194
|
+
expect( event.group ).to eq( 'control' )
|
195
|
+
end
|
196
|
+
|
197
|
+
|
184
198
|
it "can create a valid WHISPER event" do
|
185
199
|
event = factory.whisper
|
186
200
|
|
@@ -193,6 +207,14 @@ RSpec.describe( Zyre::Testing ) do
|
|
193
207
|
end
|
194
208
|
|
195
209
|
|
210
|
+
it "can create a valid WHISPER event with a multipart msg" do
|
211
|
+
event = factory.whisper( msg: %w[three times fool] )
|
212
|
+
|
213
|
+
expect( event ).to be_multipart
|
214
|
+
expect( event.multipart_msg ).to eq( %w[three times fool] )
|
215
|
+
end
|
216
|
+
|
217
|
+
|
196
218
|
it "can create a valid WHISPER event with overridden config" do
|
197
219
|
uuid = SecureRandom.uuid.tr( '-', '' )
|
198
220
|
event = factory.whisper( peer_uuid: uuid, msg: 'stop' )
|
@@ -206,6 +228,20 @@ RSpec.describe( Zyre::Testing ) do
|
|
206
228
|
end
|
207
229
|
|
208
230
|
|
231
|
+
it "can override WHISPER event message using positional parameters" do
|
232
|
+
event = factory.whisper( 'ignored', 'data1', 'data2' )
|
233
|
+
|
234
|
+
expect( event ).to be_a( Zyre::Event::Whisper )
|
235
|
+
expect( event.peer_uuid ).to eq( factory.peer_uuid )
|
236
|
+
expect( event.peer_name ).to eq( 'lancer-6' )
|
237
|
+
expect( event.headers ).to be_empty
|
238
|
+
expect( event.msg ).to eq( 'data1' )
|
239
|
+
expect( event ).to be_multipart
|
240
|
+
expect( event.multipart_msg ).to eq( ['data1', 'data2'] )
|
241
|
+
expect( event.group ).to be_nil
|
242
|
+
end
|
243
|
+
|
244
|
+
|
209
245
|
it "can create a valid LEAVE event" do
|
210
246
|
event = factory.leave
|
211
247
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: zyre
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Granger
|
@@ -33,7 +33,7 @@ cert_chain:
|
|
33
33
|
83uuAYSy65yXDGXXPVBeKPVnYrqp91pqpS5Nh7wfuiCrE8lgU8PATh7K4BV1UhAT
|
34
34
|
0MHbAT42wTYkfUj3
|
35
35
|
-----END CERTIFICATE-----
|
36
|
-
date: 2020-11-
|
36
|
+
date: 2020-11-09 00:00:00.000000000 Z
|
37
37
|
dependencies:
|
38
38
|
- !ruby/object:Gem::Dependency
|
39
39
|
name: loggability
|
metadata.gz.sig
CHANGED
Binary file
|