erlectricity 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CONTRIBUTORS +2 -0
- data/History.txt +1 -0
- data/Manifest.txt +5 -1
- data/Rakefile +1 -1
- data/examples/gruff/gruff_provider.rb +11 -11
- data/examples/tinderl/tinderl.erl +4 -0
- data/examples/tinderl/tinderl.rb +10 -6
- data/ext/decoder.c +389 -0
- data/ext/extconf.rb +11 -0
- data/lib/erlectricity.rb +11 -2
- data/lib/erlectricity/condition.rb +24 -21
- data/lib/erlectricity/conditions/hash.rb +2 -3
- data/lib/erlectricity/conditions/static.rb +2 -6
- data/lib/erlectricity/conditions/type.rb +3 -5
- data/lib/erlectricity/encoder.rb +1 -1
- data/lib/erlectricity/matcher.rb +6 -28
- data/lib/erlectricity/receiver.rb +9 -4
- data/lib/erlectricity/types/list.rb +1 -0
- data/lib/erlectricity/version.rb +1 -1
- data/test/condition_spec.rb +11 -16
- data/test/decode_spec.rb +1 -11
- data/test/encode_spec.rb +7 -0
- data/test/matcher_spec.rb +0 -4
- data/test/receiver_spec.rb +23 -23
- data/test/test_helper.rb +6 -1
- metadata +16 -8
- data/lib/erlectricity/match_context.rb +0 -20
data/CONTRIBUTORS
ADDED
data/History.txt
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
|
data/Manifest.txt
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
CONTRIBUTORS
|
2
|
+
History.txt
|
1
3
|
Manifest.txt
|
2
4
|
README.txt
|
3
5
|
Rakefile
|
@@ -8,6 +10,8 @@ examples/gruff/stat_run.erl
|
|
8
10
|
examples/gruff/stat_writer.erl
|
9
11
|
examples/tinderl/tinderl.erl
|
10
12
|
examples/tinderl/tinderl.rb
|
13
|
+
ext/decoder.c
|
14
|
+
ext/extconf.rb
|
11
15
|
lib/erlectricity.rb
|
12
16
|
lib/erlectricity/condition.rb
|
13
17
|
lib/erlectricity/conditions/hash.rb
|
@@ -19,11 +23,11 @@ lib/erlectricity/encoder.rb
|
|
19
23
|
lib/erlectricity/errors/decode_error.rb
|
20
24
|
lib/erlectricity/errors/encode_error.rb
|
21
25
|
lib/erlectricity/errors/erlectricity_error.rb
|
22
|
-
lib/erlectricity/match_context.rb
|
23
26
|
lib/erlectricity/matcher.rb
|
24
27
|
lib/erlectricity/port.rb
|
25
28
|
lib/erlectricity/receiver.rb
|
26
29
|
lib/erlectricity/types/function.rb
|
30
|
+
lib/erlectricity/types/list.rb
|
27
31
|
lib/erlectricity/types/new_function.rb
|
28
32
|
lib/erlectricity/types/new_reference.rb
|
29
33
|
lib/erlectricity/types/pid.rb
|
data/Rakefile
CHANGED
@@ -49,7 +49,7 @@ hoe = Hoe.new(GEM_NAME, VERS) do |p|
|
|
49
49
|
|
50
50
|
# == Optional
|
51
51
|
p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
|
52
|
-
|
52
|
+
p.extra_deps = [ ] # An array of rubygem dependencies [name, version], e.g. [ ['active_support', '>= 1.3.1'] ]
|
53
53
|
#p.spec_extras = {} # A hash of extra values to set in the gemspec.
|
54
54
|
end
|
55
55
|
|
@@ -1,36 +1,36 @@
|
|
1
1
|
$:.unshift(File.dirname(__FILE__) + "/../../lib/")
|
2
|
-
require 'rubygems'
|
3
2
|
require 'erlectricity'
|
3
|
+
require 'rubygems'
|
4
4
|
require 'gruff'
|
5
5
|
|
6
|
-
receive do
|
6
|
+
receive do |f|
|
7
7
|
|
8
8
|
|
9
9
|
|
10
|
-
|
10
|
+
f.when(:plot, String, Symbol, String) do |name, style, font|
|
11
11
|
graph = Gruff.const_get(style).new
|
12
12
|
graph.title = name
|
13
13
|
graph.font = font
|
14
14
|
graph.legend_font_size = 10
|
15
15
|
|
16
16
|
|
17
|
-
receive do
|
18
|
-
|
17
|
+
f.receive do |g|
|
18
|
+
g.when(:data, Symbol, Array) do |name, points|
|
19
19
|
graph.data name, points
|
20
|
-
receive_loop
|
20
|
+
g.receive_loop
|
21
21
|
end
|
22
22
|
|
23
|
-
|
23
|
+
g.when(:labels, Erl.hash) do |label_data|
|
24
24
|
graph.labels = label_data
|
25
|
-
receive_loop
|
25
|
+
g.receive_loop
|
26
26
|
end
|
27
27
|
|
28
|
-
|
28
|
+
g.when(:end){ :ok }
|
29
29
|
end
|
30
30
|
|
31
31
|
|
32
|
-
send! :result, graph.to_blob
|
33
|
-
receive_loop
|
32
|
+
f.send! :result, graph.to_blob
|
33
|
+
f.receive_loop
|
34
34
|
end
|
35
35
|
|
36
36
|
end
|
@@ -12,7 +12,11 @@ start(Domain, Email, Password, Room) ->
|
|
12
12
|
|
13
13
|
stop() -> tinderl ! stop.
|
14
14
|
|
15
|
+
|
16
|
+
speak(String) when is_list(String) -> speak(list_to_binary(String));
|
15
17
|
speak(String) when is_binary(String) -> tinderl ! {speak, self(), String}.
|
18
|
+
|
19
|
+
paste(String) when is_list(String) -> speak(list_to_binary(String));
|
16
20
|
paste(String) when is_binary(String) -> tinderl ! {paste, self(), String}.
|
17
21
|
|
18
22
|
port_loop(Port) ->
|
data/examples/tinderl/tinderl.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
$:.unshift(File.dirname(__FILE__) + "/../../lib/")
|
2
|
-
require 'rubygems'
|
3
2
|
require 'erlectricity'
|
3
|
+
require 'rubygems'
|
4
4
|
require 'tinder'
|
5
5
|
|
6
6
|
domain, email, password, room_name = *ARGV
|
@@ -8,15 +8,19 @@ campfire = Tinder::Campfire.new domain
|
|
8
8
|
campfire.login email, password
|
9
9
|
room = campfire.find_room_by_name room_name
|
10
10
|
|
11
|
-
receive do
|
12
|
-
|
11
|
+
receive do |f|
|
12
|
+
f.when(:speak, Any) do |comment|
|
13
13
|
room.speak comment
|
14
|
-
receive_loop
|
14
|
+
f.receive_loop
|
15
15
|
end
|
16
16
|
|
17
|
-
|
17
|
+
f.when(:paste, Any) do |comment|
|
18
18
|
room.paste comment
|
19
|
-
receive_loop
|
19
|
+
f.receive_loop
|
20
|
+
end
|
21
|
+
|
22
|
+
f.when(Any) do |obj|
|
23
|
+
STDERR.write obj.inspect
|
20
24
|
end
|
21
25
|
end
|
22
26
|
|
data/ext/decoder.c
ADDED
@@ -0,0 +1,389 @@
|
|
1
|
+
#include "ruby.h"
|
2
|
+
#include <string.h>
|
3
|
+
|
4
|
+
#define ERL_VERSION 131
|
5
|
+
#define ERL_SMALL_INT 97
|
6
|
+
#define ERL_INT 98
|
7
|
+
#define ERL_SMALL_BIGNUM 110
|
8
|
+
#define ERL_LARGE_BIGNUM 111
|
9
|
+
#define ERL_FLOAT 99
|
10
|
+
#define ERL_ATOM 100
|
11
|
+
#define ERL_REF 101
|
12
|
+
#define ERL_NEW_REF 114
|
13
|
+
#define ERL_PORT 102
|
14
|
+
#define ERL_PID 103
|
15
|
+
#define ERL_SMALL_TUPLE 104
|
16
|
+
#define ERL_LARGE_TUPLE 105
|
17
|
+
#define ERL_NIL 106
|
18
|
+
#define ERL_STRING 107
|
19
|
+
#define ERL_LIST 108
|
20
|
+
#define ERL_BIN 109
|
21
|
+
#define ERL_FUN 117
|
22
|
+
#define ERL_NEW_FUN 112
|
23
|
+
|
24
|
+
static VALUE mErlectricity;
|
25
|
+
static VALUE cDecoder;
|
26
|
+
void Init_decoder();
|
27
|
+
|
28
|
+
VALUE method_read_any_from(VALUE klass, VALUE rString);
|
29
|
+
|
30
|
+
VALUE read_any_raw(unsigned char **pData);
|
31
|
+
|
32
|
+
// checkers
|
33
|
+
|
34
|
+
void check_int(int num) {
|
35
|
+
char buf[17];
|
36
|
+
sprintf(buf, "%u", num);
|
37
|
+
rb_raise(rb_eStandardError, buf);
|
38
|
+
}
|
39
|
+
|
40
|
+
void check_str(char *str) {
|
41
|
+
rb_raise(rb_eStandardError, str);
|
42
|
+
}
|
43
|
+
|
44
|
+
// string peekers/readers
|
45
|
+
|
46
|
+
unsigned int peek_1(unsigned char **pData) {
|
47
|
+
return (unsigned int) **pData;
|
48
|
+
}
|
49
|
+
|
50
|
+
unsigned int peek_2(unsigned char **pData) {
|
51
|
+
return (unsigned int) ((**pData << 8) + *(*pData + 1));
|
52
|
+
}
|
53
|
+
|
54
|
+
unsigned int peek_4(unsigned char **pData) {
|
55
|
+
return (unsigned int) ((**pData << 24) + (*(*pData + 1) << 16) + (*(*pData + 2) << 8) + *(*pData + 3));
|
56
|
+
}
|
57
|
+
|
58
|
+
unsigned int read_1(unsigned char **pData) {
|
59
|
+
unsigned int val = peek_1(pData);
|
60
|
+
*pData += 1;
|
61
|
+
return val;
|
62
|
+
}
|
63
|
+
|
64
|
+
unsigned int read_2(unsigned char **pData) {
|
65
|
+
unsigned int val = peek_2(pData);
|
66
|
+
*pData += 2;
|
67
|
+
return val;
|
68
|
+
}
|
69
|
+
|
70
|
+
unsigned int read_4(unsigned char **pData) {
|
71
|
+
unsigned int val = peek_4(pData);
|
72
|
+
*pData += 4;
|
73
|
+
return val;
|
74
|
+
}
|
75
|
+
|
76
|
+
// tuples, lists
|
77
|
+
|
78
|
+
VALUE read_small_tuple(unsigned char **pData) {
|
79
|
+
if(read_1(pData) != ERL_SMALL_TUPLE) {
|
80
|
+
rb_raise(rb_eStandardError, "Invalid Type, not a small tuple");
|
81
|
+
}
|
82
|
+
|
83
|
+
int arity = read_1(pData);
|
84
|
+
|
85
|
+
VALUE array = rb_ary_new2(arity);
|
86
|
+
|
87
|
+
int i;
|
88
|
+
for(i = 0; i < arity; ++i) {
|
89
|
+
rb_ary_store(array, i, read_any_raw(pData));
|
90
|
+
}
|
91
|
+
|
92
|
+
return array;
|
93
|
+
}
|
94
|
+
|
95
|
+
VALUE read_large_tuple(unsigned char **pData) {
|
96
|
+
if(read_1(pData) != ERL_LARGE_TUPLE) {
|
97
|
+
rb_raise(rb_eStandardError, "Invalid Type, not a large tuple");
|
98
|
+
}
|
99
|
+
|
100
|
+
int arity = read_4(pData);
|
101
|
+
|
102
|
+
VALUE array = rb_ary_new2(arity);
|
103
|
+
|
104
|
+
int i;
|
105
|
+
for(i = 0; i < arity; ++i) {
|
106
|
+
rb_ary_store(array, i, read_any_raw(pData));
|
107
|
+
}
|
108
|
+
|
109
|
+
return array;
|
110
|
+
}
|
111
|
+
|
112
|
+
VALUE read_list(unsigned char **pData) {
|
113
|
+
if(read_1(pData) != ERL_LIST) {
|
114
|
+
rb_raise(rb_eStandardError, "Invalid Type, not an erlang list");
|
115
|
+
}
|
116
|
+
|
117
|
+
int size = read_4(pData);
|
118
|
+
|
119
|
+
VALUE array = rb_ary_new2(size);
|
120
|
+
|
121
|
+
int i;
|
122
|
+
for(i = 0; i < size; ++i) {
|
123
|
+
rb_ary_store(array, i, read_any_raw(pData));
|
124
|
+
}
|
125
|
+
|
126
|
+
return array;
|
127
|
+
}
|
128
|
+
|
129
|
+
// primitives
|
130
|
+
|
131
|
+
void read_string_raw(unsigned char *dest, unsigned char **pData, int length) {
|
132
|
+
memcpy((char *) dest, (char *) *pData, length);
|
133
|
+
*(dest + length) = (unsigned char) 0;
|
134
|
+
*pData += length;
|
135
|
+
}
|
136
|
+
|
137
|
+
VALUE read_bin(unsigned char **pData) {
|
138
|
+
if(read_1(pData) != ERL_BIN) {
|
139
|
+
rb_raise(rb_eStandardError, "Invalid Type, not an erlang binary");
|
140
|
+
}
|
141
|
+
|
142
|
+
int length = read_4(pData);
|
143
|
+
|
144
|
+
unsigned char buf[length + 1];
|
145
|
+
read_string_raw(buf, pData, length);
|
146
|
+
|
147
|
+
return rb_str_new2((char *) buf);
|
148
|
+
}
|
149
|
+
|
150
|
+
VALUE read_string(unsigned char **pData) {
|
151
|
+
if(read_1(pData) != ERL_STRING) {
|
152
|
+
rb_raise(rb_eStandardError, "Invalid Type, not an erlang string");
|
153
|
+
}
|
154
|
+
|
155
|
+
int length = read_2(pData);
|
156
|
+
|
157
|
+
unsigned char buf[length + 1];
|
158
|
+
read_string_raw(buf, pData, length);
|
159
|
+
|
160
|
+
VALUE array = rb_ary_new2(length);
|
161
|
+
|
162
|
+
int i = 0;
|
163
|
+
for(i; i < length; ++i) {
|
164
|
+
rb_ary_store(array, i, INT2NUM(*(buf + i)));
|
165
|
+
}
|
166
|
+
|
167
|
+
return array;
|
168
|
+
}
|
169
|
+
|
170
|
+
VALUE read_atom(unsigned char **pData) {
|
171
|
+
if(read_1(pData) != ERL_ATOM) {
|
172
|
+
rb_raise(rb_eStandardError, "Invalid Type, not an atom");
|
173
|
+
}
|
174
|
+
|
175
|
+
int length = read_2(pData);
|
176
|
+
|
177
|
+
unsigned char buf[length + 1];
|
178
|
+
read_string_raw(buf, pData, length);
|
179
|
+
|
180
|
+
return ID2SYM(rb_intern((char *) buf));
|
181
|
+
}
|
182
|
+
|
183
|
+
VALUE read_small_int(unsigned char **pData) {
|
184
|
+
if(read_1(pData) != ERL_SMALL_INT) {
|
185
|
+
rb_raise(rb_eStandardError, "Invalid Type, not a small int");
|
186
|
+
}
|
187
|
+
|
188
|
+
int value = read_1(pData);
|
189
|
+
|
190
|
+
return INT2FIX(value);
|
191
|
+
}
|
192
|
+
|
193
|
+
VALUE read_int(unsigned char **pData) {
|
194
|
+
if(read_1(pData) != ERL_INT) {
|
195
|
+
rb_raise(rb_eStandardError, "Invalid Type, not an int");
|
196
|
+
}
|
197
|
+
|
198
|
+
long long value = read_4(pData);
|
199
|
+
|
200
|
+
long long negative = ((value >> 31) & 0x1 == 1);
|
201
|
+
|
202
|
+
if(negative) {
|
203
|
+
value = (value - ((long long) 1 << 32));
|
204
|
+
}
|
205
|
+
|
206
|
+
return INT2FIX(value);
|
207
|
+
}
|
208
|
+
|
209
|
+
VALUE read_small_bignum(unsigned char **pData) {
|
210
|
+
if(read_1(pData) != ERL_SMALL_BIGNUM) {
|
211
|
+
rb_raise(rb_eStandardError, "Invalid Type, not a small bignum");
|
212
|
+
}
|
213
|
+
|
214
|
+
unsigned int size = read_1(pData);
|
215
|
+
unsigned int sign = read_1(pData);
|
216
|
+
|
217
|
+
VALUE num = INT2NUM(0);
|
218
|
+
VALUE tmp;
|
219
|
+
|
220
|
+
unsigned char buf[size + 1];
|
221
|
+
read_string_raw(buf, pData, size);
|
222
|
+
|
223
|
+
int i;
|
224
|
+
for(i = 0; i < size; ++i) {
|
225
|
+
tmp = INT2FIX(*(buf + i));
|
226
|
+
tmp = rb_funcall(tmp, rb_intern("<<"), 1, INT2NUM(i * 8));
|
227
|
+
num = rb_funcall(num, rb_intern("+"), 1, tmp);
|
228
|
+
}
|
229
|
+
|
230
|
+
if(sign) {
|
231
|
+
num = rb_funcall(num, rb_intern("*"), 1, INT2NUM(-1));
|
232
|
+
}
|
233
|
+
|
234
|
+
return num;
|
235
|
+
}
|
236
|
+
|
237
|
+
VALUE read_large_bignum(unsigned char **pData) {
|
238
|
+
if(read_1(pData) != ERL_LARGE_BIGNUM) {
|
239
|
+
rb_raise(rb_eStandardError, "Invalid Type, not a small bignum");
|
240
|
+
}
|
241
|
+
|
242
|
+
unsigned int size = read_4(pData);
|
243
|
+
unsigned int sign = read_1(pData);
|
244
|
+
|
245
|
+
VALUE num = INT2NUM(0);
|
246
|
+
VALUE tmp;
|
247
|
+
|
248
|
+
unsigned char buf[size + 1];
|
249
|
+
read_string_raw(buf, pData, size);
|
250
|
+
|
251
|
+
int i;
|
252
|
+
for(i = 0; i < size; ++i) {
|
253
|
+
tmp = INT2FIX(*(buf + i));
|
254
|
+
tmp = rb_funcall(tmp, rb_intern("<<"), 1, INT2NUM(i * 8));
|
255
|
+
|
256
|
+
num = rb_funcall(num, rb_intern("+"), 1, tmp);
|
257
|
+
}
|
258
|
+
|
259
|
+
if(sign) {
|
260
|
+
num = rb_funcall(num, rb_intern("*"), 1, INT2NUM(-1));
|
261
|
+
}
|
262
|
+
|
263
|
+
return num;
|
264
|
+
}
|
265
|
+
|
266
|
+
VALUE read_float(unsigned char **pData) {
|
267
|
+
if(read_1(pData) != ERL_FLOAT) {
|
268
|
+
rb_raise(rb_eStandardError, "Invalid Type, not a float");
|
269
|
+
}
|
270
|
+
|
271
|
+
unsigned char buf[32];
|
272
|
+
read_string_raw(buf, pData, 31);
|
273
|
+
|
274
|
+
VALUE rString = rb_str_new2((char *) buf);
|
275
|
+
|
276
|
+
return rb_funcall(rString, rb_intern("to_f"), 0);
|
277
|
+
}
|
278
|
+
|
279
|
+
VALUE read_nil(unsigned char **pData) {
|
280
|
+
if(read_1(pData) != ERL_NIL) {
|
281
|
+
rb_raise(rb_eStandardError, "Invalid Type, not a nil list");
|
282
|
+
}
|
283
|
+
|
284
|
+
return rb_ary_new2(0);
|
285
|
+
}
|
286
|
+
|
287
|
+
// specials
|
288
|
+
|
289
|
+
VALUE read_pid(unsigned char **pData) {
|
290
|
+
if(read_1(pData) != ERL_PID) {
|
291
|
+
rb_raise(rb_eStandardError, "Invalid Type, not a pid");
|
292
|
+
}
|
293
|
+
|
294
|
+
VALUE node = read_atom(pData);
|
295
|
+
VALUE id = INT2NUM(read_4(pData));
|
296
|
+
VALUE serial = INT2NUM(read_4(pData));
|
297
|
+
VALUE creation = INT2FIX(read_1(pData));
|
298
|
+
|
299
|
+
VALUE pid_class = rb_const_get(mErlectricity, rb_intern("Pid"));
|
300
|
+
return rb_funcall(pid_class, rb_intern("new"), 4, node, id, serial, creation);
|
301
|
+
}
|
302
|
+
|
303
|
+
VALUE read_new_reference(unsigned char **pData) {
|
304
|
+
if(read_1(pData) != ERL_NEW_REF) {
|
305
|
+
rb_raise(rb_eStandardError, "Invalid Type, not a new-style reference");
|
306
|
+
}
|
307
|
+
|
308
|
+
int size = read_2(pData);
|
309
|
+
VALUE node = read_atom(pData);
|
310
|
+
VALUE creation = INT2FIX(read_1(pData));
|
311
|
+
|
312
|
+
VALUE id = rb_ary_new2(size);
|
313
|
+
int i;
|
314
|
+
for(i = 0; i < size; ++i) {
|
315
|
+
rb_ary_store(id, i, INT2NUM(read_4(pData)));
|
316
|
+
}
|
317
|
+
|
318
|
+
VALUE newref_class = rb_const_get(mErlectricity, rb_intern("NewReference"));
|
319
|
+
return rb_funcall(newref_class, rb_intern("new"), 3, node, creation, id);
|
320
|
+
}
|
321
|
+
|
322
|
+
// read_any_raw
|
323
|
+
|
324
|
+
VALUE read_any_raw(unsigned char **pData) {
|
325
|
+
switch(peek_1(pData)) {
|
326
|
+
case ERL_SMALL_INT:
|
327
|
+
return read_small_int(pData);
|
328
|
+
break;
|
329
|
+
case ERL_INT:
|
330
|
+
return read_int(pData);
|
331
|
+
break;
|
332
|
+
case ERL_FLOAT:
|
333
|
+
return read_float(pData);
|
334
|
+
break;
|
335
|
+
case ERL_ATOM:
|
336
|
+
return read_atom(pData);
|
337
|
+
break;
|
338
|
+
case ERL_PID:
|
339
|
+
return read_pid(pData);
|
340
|
+
break;
|
341
|
+
case ERL_SMALL_TUPLE:
|
342
|
+
return read_small_tuple(pData);
|
343
|
+
break;
|
344
|
+
case ERL_LARGE_TUPLE:
|
345
|
+
return read_large_tuple(pData);
|
346
|
+
break;
|
347
|
+
case ERL_NIL:
|
348
|
+
return read_nil(pData);
|
349
|
+
break;
|
350
|
+
case ERL_STRING:
|
351
|
+
return read_string(pData);
|
352
|
+
break;
|
353
|
+
case ERL_LIST:
|
354
|
+
return read_list(pData);
|
355
|
+
break;
|
356
|
+
case ERL_BIN:
|
357
|
+
return read_bin(pData);
|
358
|
+
break;
|
359
|
+
case ERL_SMALL_BIGNUM:
|
360
|
+
return read_small_bignum(pData);
|
361
|
+
break;
|
362
|
+
case ERL_LARGE_BIGNUM:
|
363
|
+
return read_large_bignum(pData);
|
364
|
+
break;
|
365
|
+
case ERL_NEW_REF:
|
366
|
+
return read_new_reference(pData);
|
367
|
+
break;
|
368
|
+
}
|
369
|
+
return Qnil;
|
370
|
+
}
|
371
|
+
|
372
|
+
VALUE method_read_any_from(VALUE klass, VALUE rString) {
|
373
|
+
unsigned char *data = (unsigned char *) StringValuePtr(rString);
|
374
|
+
|
375
|
+
unsigned char **pData = &data;
|
376
|
+
|
377
|
+
// check protocol version
|
378
|
+
if(read_1(pData) != ERL_VERSION) {
|
379
|
+
rb_raise(rb_eStandardError, "Bad Magic");
|
380
|
+
}
|
381
|
+
|
382
|
+
return read_any_raw(pData);
|
383
|
+
}
|
384
|
+
|
385
|
+
void Init_decoder() {
|
386
|
+
mErlectricity = rb_const_get(rb_cObject, rb_intern("Erlectricity"));
|
387
|
+
cDecoder = rb_define_class_under(mErlectricity, "Decoder", rb_cObject);
|
388
|
+
rb_define_singleton_method(cDecoder, "read_any_from", method_read_any_from, 1);
|
389
|
+
}
|
data/ext/extconf.rb
ADDED
data/lib/erlectricity.rb
CHANGED
@@ -3,13 +3,20 @@ require 'erlectricity/constants'
|
|
3
3
|
require 'erlectricity/types/new_reference'
|
4
4
|
require 'erlectricity/types/pid'
|
5
5
|
require 'erlectricity/types/function'
|
6
|
+
require 'erlectricity/types/list'
|
7
|
+
|
8
|
+
begin
|
9
|
+
#try to load the decoder C extension
|
10
|
+
require 'decoder'
|
11
|
+
rescue LoadError
|
12
|
+
#load the pure ruby instead
|
13
|
+
require 'erlectricity/decoder'
|
14
|
+
end
|
6
15
|
|
7
|
-
require 'erlectricity/decoder'
|
8
16
|
require 'erlectricity/encoder'
|
9
17
|
|
10
18
|
require 'erlectricity/port'
|
11
19
|
require 'erlectricity/matcher'
|
12
|
-
require 'erlectricity/match_context'
|
13
20
|
|
14
21
|
require 'erlectricity/condition'
|
15
22
|
require 'erlectricity/conditions/hash'
|
@@ -21,3 +28,5 @@ require 'erlectricity/receiver'
|
|
21
28
|
require 'erlectricity/errors/erlectricity_error'
|
22
29
|
require 'erlectricity/errors/decode_error'
|
23
30
|
require 'erlectricity/errors/encode_error'
|
31
|
+
|
32
|
+
Erl = Erlectricity
|
@@ -1,48 +1,51 @@
|
|
1
1
|
module Erlectricity
|
2
2
|
class Condition
|
3
|
-
attr_accessor :binding_name
|
4
3
|
|
5
|
-
def initialize
|
6
|
-
self.binding_name = binding_name
|
4
|
+
def initialize
|
7
5
|
end
|
8
6
|
|
9
|
-
def
|
10
|
-
|
7
|
+
def binding_for(arg)
|
8
|
+
nil
|
11
9
|
end
|
12
10
|
|
13
11
|
def satisfies?(arg)
|
14
12
|
false
|
15
13
|
end
|
16
|
-
|
14
|
+
|
15
|
+
alias === satisfies?
|
17
16
|
end
|
18
17
|
|
19
18
|
module Conditions
|
20
|
-
def atom(
|
21
|
-
TypeCondition.new(Symbol
|
19
|
+
def atom()
|
20
|
+
TypeCondition.new(Symbol)
|
22
21
|
end
|
23
22
|
|
24
|
-
def any(
|
25
|
-
TypeCondition.new(Object
|
23
|
+
def any()
|
24
|
+
TypeCondition.new(Object)
|
26
25
|
end
|
27
26
|
|
28
|
-
def number(
|
29
|
-
TypeCondition.new(Fixnum
|
27
|
+
def number()
|
28
|
+
TypeCondition.new(Fixnum)
|
30
29
|
end
|
31
30
|
|
32
|
-
def pid(
|
33
|
-
TypeCondition.new(Erlectricity::Pid
|
31
|
+
def pid()
|
32
|
+
TypeCondition.new(Erlectricity::Pid)
|
34
33
|
end
|
35
34
|
|
36
|
-
def string(
|
37
|
-
TypeCondition.new(String
|
35
|
+
def string()
|
36
|
+
TypeCondition.new(String)
|
38
37
|
end
|
39
38
|
|
40
|
-
def list(
|
41
|
-
TypeCondition.new(Array
|
39
|
+
def list()
|
40
|
+
TypeCondition.new(Array)
|
42
41
|
end
|
43
42
|
|
44
|
-
def hash(
|
45
|
-
HashCondition.new(
|
43
|
+
def hash()
|
44
|
+
HashCondition.new()
|
46
45
|
end
|
47
46
|
end
|
48
|
-
|
47
|
+
|
48
|
+
extend Conditions
|
49
|
+
end
|
50
|
+
|
51
|
+
Any = Object
|
@@ -6,10 +6,9 @@ class HashCondition < Condition
|
|
6
6
|
arg.all?{|x| x.class == Array && x.length == 2}
|
7
7
|
end
|
8
8
|
|
9
|
-
def
|
10
|
-
return {} unless self.binding_name
|
9
|
+
def binding_for(arg)
|
11
10
|
flattened = arg.inject([]){|memo, kv| memo + kv}
|
12
|
-
|
11
|
+
Hash[*flattened]
|
13
12
|
end
|
14
13
|
end
|
15
14
|
end
|
@@ -1,17 +1,13 @@
|
|
1
1
|
module Erlectricity
|
2
2
|
class StaticCondition < Condition
|
3
3
|
attr_accessor :value
|
4
|
-
def initialize(value
|
4
|
+
def initialize(value)
|
5
5
|
self.value = value
|
6
|
-
super(name)
|
7
6
|
end
|
8
7
|
|
9
8
|
def satisfies?(arg)
|
10
9
|
arg.eql? value
|
11
10
|
end
|
12
|
-
|
13
|
-
def bindings_for(arg)
|
14
|
-
{}
|
15
|
-
end
|
11
|
+
|
16
12
|
end
|
17
13
|
end
|
@@ -2,18 +2,16 @@ module Erlectricity
|
|
2
2
|
class TypeCondition < Condition
|
3
3
|
attr_accessor :type
|
4
4
|
|
5
|
-
def initialize(type
|
5
|
+
def initialize(type)
|
6
6
|
self.type = type
|
7
|
-
super(name)
|
8
7
|
end
|
9
8
|
|
10
9
|
def satisfies?(arg)
|
11
10
|
arg.is_a? self.type
|
12
11
|
end
|
13
12
|
|
14
|
-
def
|
15
|
-
|
16
|
-
{self.binding_name => arg}
|
13
|
+
def binding_for(arg)
|
14
|
+
arg
|
17
15
|
end
|
18
16
|
end
|
19
17
|
end
|
data/lib/erlectricity/encoder.rb
CHANGED
@@ -19,6 +19,7 @@ class Encoder
|
|
19
19
|
when Float then write_float(obj)
|
20
20
|
when Erlectricity::NewReference then write_new_reference(obj)
|
21
21
|
when Erlectricity::Pid then write_pid(obj)
|
22
|
+
when Erlectricity::List then write_list(obj)
|
22
23
|
when Array then write_tuple(obj)
|
23
24
|
when String then write_binary(obj)
|
24
25
|
else
|
@@ -106,7 +107,6 @@ class Encoder
|
|
106
107
|
fail(data) unless data.is_a? Array
|
107
108
|
write_1 NIL and return if data.empty?
|
108
109
|
|
109
|
-
#NOTE: we do not ever encode as the string format.
|
110
110
|
write_1 LIST
|
111
111
|
write_4 data.length
|
112
112
|
data.each{|e| write_any_raw e }
|
data/lib/erlectricity/matcher.rb
CHANGED
@@ -10,10 +10,8 @@ class Matcher
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def run(arg)
|
13
|
-
|
14
|
-
|
15
|
-
populate_context context, arg
|
16
|
-
context.instance_eval &block
|
13
|
+
args = get_bound arg
|
14
|
+
block.call *args
|
17
15
|
end
|
18
16
|
|
19
17
|
def matches?(arg)
|
@@ -28,33 +26,13 @@ class Matcher
|
|
28
26
|
|
29
27
|
|
30
28
|
private
|
31
|
-
|
29
|
+
|
30
|
+
def get_bound(arg)
|
32
31
|
if @condition.is_a?(Array) && arg.is_a?(Array)
|
33
|
-
@condition.zip(arg).
|
32
|
+
@condition.zip(arg).map{|l,r| l.binding_for r}.compact
|
34
33
|
else
|
35
|
-
|
34
|
+
@condition.binding_for(arg)
|
36
35
|
end
|
37
36
|
end
|
38
|
-
|
39
|
-
def set_binding(context, condition, arg)
|
40
|
-
condition.bindings_for(arg).each do |k, v|
|
41
|
-
add_to_context(context, k, v)
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
def add_to_context(context, name, value)
|
46
|
-
return if name.nil?
|
47
|
-
|
48
|
-
context.instance_eval <<-EOS
|
49
|
-
def #{name}
|
50
|
-
@#{name}
|
51
|
-
end
|
52
|
-
def #{name}= (value)
|
53
|
-
@#{name} = value
|
54
|
-
end
|
55
|
-
EOS
|
56
|
-
|
57
|
-
context.send(:"#{name}=", value)
|
58
|
-
end
|
59
37
|
end
|
60
38
|
end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
module Erlectricity
|
2
2
|
class Receiver
|
3
|
-
include Conditions
|
4
3
|
|
5
4
|
attr_accessor :port
|
6
5
|
attr_accessor :parent
|
@@ -13,7 +12,7 @@ class Receiver
|
|
13
12
|
@port = port
|
14
13
|
@parent = parent
|
15
14
|
@matchers = []
|
16
|
-
|
15
|
+
block.call self if block
|
17
16
|
end
|
18
17
|
|
19
18
|
def process(arg)
|
@@ -27,8 +26,14 @@ class Receiver
|
|
27
26
|
end
|
28
27
|
end
|
29
28
|
|
30
|
-
def
|
31
|
-
args = args.map
|
29
|
+
def when(*args, &block)
|
30
|
+
args = args.map do |a|
|
31
|
+
case a
|
32
|
+
when Condition then a
|
33
|
+
when Class then TypeCondition.new(a)
|
34
|
+
else StaticCondition.new(a)
|
35
|
+
end
|
36
|
+
end
|
32
37
|
|
33
38
|
args = args.first if args.length == 1
|
34
39
|
@matchers << Matcher.new(self, args, block)
|
@@ -0,0 +1 @@
|
|
1
|
+
class Erlectricity::List < Array ; end
|
data/lib/erlectricity/version.rb
CHANGED
data/test/condition_spec.rb
CHANGED
@@ -7,16 +7,16 @@ context "Erlectricity::StaticConditions" do
|
|
7
7
|
Erlectricity::StaticCondition.new(3).satisfies?(3).should == true
|
8
8
|
end
|
9
9
|
|
10
|
-
specify "should not satisfy on
|
10
|
+
specify "should not satisfy on different values" do
|
11
11
|
Erlectricity::StaticCondition.new(:foo).satisfies?("foo").should == false
|
12
12
|
Erlectricity::StaticCondition.new([:foo]).satisfies?(:foo).should == false
|
13
13
|
Erlectricity::StaticCondition.new(Object.new).satisfies?(Object.new).should == false
|
14
14
|
Erlectricity::StaticCondition.new(3).satisfies?(3.0).should == false
|
15
15
|
end
|
16
16
|
|
17
|
-
specify "should not produce any bindings
|
18
|
-
s = Erlectricity::StaticCondition.new(:foo
|
19
|
-
s.
|
17
|
+
specify "should not produce any bindings" do
|
18
|
+
s = Erlectricity::StaticCondition.new(:foo)
|
19
|
+
s.binding_for(:foo).should == nil
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
@@ -42,17 +42,12 @@ context "Erlectricity::TypeConditions" do
|
|
42
42
|
Erlectricity::TypeCondition.new(Fixnum).satisfies?(3.0).should == false
|
43
43
|
end
|
44
44
|
|
45
|
-
specify "should bind the arg
|
46
|
-
s = Erlectricity::TypeCondition.new(Symbol, :bound_name)
|
47
|
-
s.bindings_for(:foo).should == {:bound_name => :foo}
|
48
|
-
s.bindings_for(:bar).should == {:bound_name => :bar}
|
49
|
-
end
|
50
|
-
|
51
|
-
specify "should not bind anything if no binding name is specified" do
|
52
|
-
|
45
|
+
specify "should bind the arg with no transormations" do
|
53
46
|
s = Erlectricity::TypeCondition.new(Symbol)
|
54
|
-
s.
|
47
|
+
s.binding_for(:foo).should == :foo
|
48
|
+
s.binding_for(:bar).should == :bar
|
55
49
|
end
|
50
|
+
|
56
51
|
end
|
57
52
|
|
58
53
|
context "Erlectricity::HashConditions" do
|
@@ -71,8 +66,8 @@ context "Erlectricity::HashConditions" do
|
|
71
66
|
Erlectricity::HashCondition.new.satisfies?(3.0).should == false
|
72
67
|
end
|
73
68
|
|
74
|
-
specify "should
|
75
|
-
s = Erlectricity::HashCondition.new(
|
76
|
-
s.
|
69
|
+
specify "should bind to a Hash" do
|
70
|
+
s = Erlectricity::HashCondition.new()
|
71
|
+
s.binding_for([[:foo, 3], [:bar, [3,4,5]]]).should == {:foo => 3, :bar => [3,4,5] }
|
77
72
|
end
|
78
73
|
end
|
data/test/decode_spec.rb
CHANGED
@@ -1,16 +1,7 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/test_helper.rb'
|
2
2
|
|
3
|
-
context "The byte reader attached to decoder" do
|
4
|
-
specify "should not advance the stream when peeking unless there arent enough bytes available" do
|
5
|
-
@decoder = Erlectricity::Decoder.new(StringIO.new('abcdefghijklmnopqrstuvwxyz'))
|
6
|
-
100.times{ @decoder.peek_1.should == 'a'[0] }
|
7
|
-
100.times{ @decoder.peek_2.should == 'ab'.unpack("n").first }
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
3
|
context "When unpacking from a binary stream" do
|
12
4
|
setup do
|
13
|
-
@decoder = Erlectricity::Decoder.new(nil)
|
14
5
|
end
|
15
6
|
|
16
7
|
specify "an erlang atom should decode to a ruby symbol" do
|
@@ -127,7 +118,6 @@ context "When unpacking from a binary stream" do
|
|
127
118
|
|
128
119
|
def get(str)
|
129
120
|
bin = run_erl("term_to_binary(#{str})")
|
130
|
-
|
131
|
-
@decoder.read_any
|
121
|
+
Erlectricity::Decoder.read_any_from(bin)
|
132
122
|
end
|
133
123
|
end
|
data/test/encode_spec.rb
CHANGED
@@ -88,6 +88,13 @@ context "When packing to a binary stream" do
|
|
88
88
|
write_any([3] * 512).should == get_erl_with_magic("{#{([3] * 512).join(',')}}")
|
89
89
|
end
|
90
90
|
|
91
|
+
specify "An Erlectricity::List should by default be written as a list" do
|
92
|
+
write_any(Erl::List.new([1,2,300])).should == get_erl_with_magic("[1,2,300]")
|
93
|
+
write_any(Erl::List.new([300] * 255)).should == get_erl_with_magic("[#{([300] * 255).join(',')}]")
|
94
|
+
write_any(Erl::List.new([300] * 256)).should == get_erl_with_magic("[#{([300] * 256).join(',')}]")
|
95
|
+
write_any(Erl::List.new([300] * 512)).should == get_erl_with_magic("[#{([300] * 512).join(',')}]")
|
96
|
+
end
|
97
|
+
|
91
98
|
specify "An array written with write_list should encode as erlang would a list" do
|
92
99
|
get{@encoder.write_list [1,2,300]}.should == get_erl("[1,2,300]")
|
93
100
|
get{@encoder.write_list [300] * 255}.should == get_erl("[#{([300] * 255).join(',')}]")
|
data/test/matcher_spec.rb
CHANGED
data/test/receiver_spec.rb
CHANGED
@@ -5,8 +5,8 @@ def simple_receiver_and_port(*terms, &block)
|
|
5
5
|
receiver = if block
|
6
6
|
Erlectricity::Receiver.new(port, &block)
|
7
7
|
else
|
8
|
-
Erlectricity::Receiver.new(port) do
|
9
|
-
|
8
|
+
Erlectricity::Receiver.new(port) do |f|
|
9
|
+
f.when Erl.any do
|
10
10
|
:matched
|
11
11
|
end
|
12
12
|
end
|
@@ -17,12 +17,12 @@ end
|
|
17
17
|
context "When a receiver is passed a message that matches two match blocks it" do
|
18
18
|
setup do
|
19
19
|
@port = FakePort.new([:foo, :foo])
|
20
|
-
@receiver = Erlectricity::Receiver.new(@port) do
|
21
|
-
|
20
|
+
@receiver = Erlectricity::Receiver.new(@port) do |f|
|
21
|
+
f.when(:foo, :foo) do
|
22
22
|
:first
|
23
23
|
end
|
24
24
|
|
25
|
-
|
25
|
+
f.when(:foo, Erl.any) do
|
26
26
|
:second
|
27
27
|
end
|
28
28
|
end
|
@@ -41,9 +41,9 @@ context "A receiver" do
|
|
41
41
|
end
|
42
42
|
|
43
43
|
specify "should process another message if the matched block returns the results of receive_loop" do
|
44
|
-
recv = simple_receiver_and_port(:foo, :bar, :baz) do
|
45
|
-
|
46
|
-
|
44
|
+
recv = simple_receiver_and_port(:foo, :bar, :baz) do |f|
|
45
|
+
f.when(:bar) { }
|
46
|
+
f.when(Erl.any) { f.receive_loop }
|
47
47
|
end
|
48
48
|
|
49
49
|
recv.run
|
@@ -52,15 +52,15 @@ context "A receiver" do
|
|
52
52
|
|
53
53
|
specify "should properly nest" do
|
54
54
|
@port = FakePort.new(:foo, :bar, :baz)
|
55
|
-
@receiver = Erlectricity::Receiver.new(@port) do
|
56
|
-
|
57
|
-
receive do
|
58
|
-
|
55
|
+
@receiver = Erlectricity::Receiver.new(@port) do |f|
|
56
|
+
f.when(:foo) do
|
57
|
+
f.receive do |g|
|
58
|
+
g.when(:bar){ :ok }
|
59
59
|
end
|
60
|
-
receive_loop
|
60
|
+
f.receive_loop
|
61
61
|
end
|
62
62
|
|
63
|
-
|
63
|
+
f.when(:baz) do
|
64
64
|
:done
|
65
65
|
end
|
66
66
|
end
|
@@ -71,15 +71,15 @@ context "A receiver" do
|
|
71
71
|
|
72
72
|
specify "should queue up skipped results and restore them when a match happens" do
|
73
73
|
@port = FakePort.new(:foo, :baz, :bar)
|
74
|
-
@receiver = Erlectricity::Receiver.new(@port) do
|
75
|
-
|
76
|
-
receive do
|
77
|
-
|
74
|
+
@receiver = Erlectricity::Receiver.new(@port) do |f|
|
75
|
+
f.when(:foo) do
|
76
|
+
f.receive do |g|
|
77
|
+
g.when(:bar){ :ok }
|
78
78
|
end
|
79
|
-
receive_loop
|
79
|
+
f.receive_loop
|
80
80
|
end
|
81
81
|
|
82
|
-
|
82
|
+
f.when(:baz) do
|
83
83
|
:done
|
84
84
|
end
|
85
85
|
end
|
@@ -91,10 +91,10 @@ context "A receiver" do
|
|
91
91
|
specify "should expose bindings to the matched block" do
|
92
92
|
@port = FakePort.new(:foo, :bar, :baz)
|
93
93
|
results = []
|
94
|
-
@receiver = Erlectricity::Receiver.new(@port) do
|
95
|
-
|
94
|
+
@receiver = Erlectricity::Receiver.new(@port) do |f|
|
95
|
+
f.when(Erl.atom) do |bindinated|
|
96
96
|
results << bindinated
|
97
|
-
receive_loop
|
97
|
+
f.receive_loop
|
98
98
|
end
|
99
99
|
end
|
100
100
|
|
data/test/test_helper.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
$:.unshift(File.dirname(__FILE__) + '/../lib')
|
2
|
+
require 'erlectricity'
|
2
3
|
require 'rubygems'
|
3
4
|
require 'test/unit'
|
4
5
|
require 'test/spec'
|
5
|
-
require 'erlectricity'
|
6
6
|
|
7
7
|
class Test::Unit::TestCase
|
8
8
|
|
@@ -10,6 +10,11 @@ class Test::Unit::TestCase
|
|
10
10
|
`erl -noshell -eval 'A = #{code.split.join(' ')}, io:put_chars(A).' -s erlang halt`
|
11
11
|
end
|
12
12
|
|
13
|
+
def encode_packet(code)
|
14
|
+
bin = run_erl("term_to_binary(#{code})")
|
15
|
+
[bin.length, bin].pack("Na#{bin.length}")
|
16
|
+
end
|
17
|
+
|
13
18
|
def word_length
|
14
19
|
(1.size * 8) - 2
|
15
20
|
end
|
metadata
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
|
-
rubygems_version: 0.9.
|
2
|
+
rubygems_version: 0.9.4
|
3
3
|
specification_version: 1
|
4
4
|
name: erlectricity
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.
|
7
|
-
date: 2007-
|
6
|
+
version: 0.2.0
|
7
|
+
date: 2007-10-29 00:00:00 -07:00
|
8
8
|
summary: A library to interface erlang and ruby through the erlang port system
|
9
9
|
require_paths:
|
10
10
|
- lib
|
11
|
+
- ext
|
11
12
|
email: nullstyle@gmail.com
|
12
13
|
homepage: http://erlectricity.rubyforge.org
|
13
14
|
rubyforge_project: erlectricity
|
@@ -29,6 +30,8 @@ post_install_message:
|
|
29
30
|
authors:
|
30
31
|
- Scott Fleckenstein
|
31
32
|
files:
|
33
|
+
- CONTRIBUTORS
|
34
|
+
- History.txt
|
32
35
|
- Manifest.txt
|
33
36
|
- README.txt
|
34
37
|
- Rakefile
|
@@ -39,6 +42,8 @@ files:
|
|
39
42
|
- examples/gruff/stat_writer.erl
|
40
43
|
- examples/tinderl/tinderl.erl
|
41
44
|
- examples/tinderl/tinderl.rb
|
45
|
+
- ext/decoder.c
|
46
|
+
- ext/extconf.rb
|
42
47
|
- lib/erlectricity.rb
|
43
48
|
- lib/erlectricity/condition.rb
|
44
49
|
- lib/erlectricity/conditions/hash.rb
|
@@ -50,11 +55,11 @@ files:
|
|
50
55
|
- lib/erlectricity/errors/decode_error.rb
|
51
56
|
- lib/erlectricity/errors/encode_error.rb
|
52
57
|
- lib/erlectricity/errors/erlectricity_error.rb
|
53
|
-
- lib/erlectricity/match_context.rb
|
54
58
|
- lib/erlectricity/matcher.rb
|
55
59
|
- lib/erlectricity/port.rb
|
56
60
|
- lib/erlectricity/receiver.rb
|
57
61
|
- lib/erlectricity/types/function.rb
|
62
|
+
- lib/erlectricity/types/list.rb
|
58
63
|
- lib/erlectricity/types/new_function.rb
|
59
64
|
- lib/erlectricity/types/new_reference.rb
|
60
65
|
- lib/erlectricity/types/pid.rb
|
@@ -73,10 +78,13 @@ files:
|
|
73
78
|
test_files:
|
74
79
|
- test/test_erlectricity.rb
|
75
80
|
- test/test_helper.rb
|
76
|
-
rdoc_options:
|
77
|
-
|
78
|
-
|
79
|
-
|
81
|
+
rdoc_options:
|
82
|
+
- --main
|
83
|
+
- README.txt
|
84
|
+
extra_rdoc_files:
|
85
|
+
- History.txt
|
86
|
+
- Manifest.txt
|
87
|
+
- README.txt
|
80
88
|
executables: []
|
81
89
|
|
82
90
|
extensions: []
|
@@ -1,20 +0,0 @@
|
|
1
|
-
module Erlectricity
|
2
|
-
class MatchContext
|
3
|
-
attr_accessor :receiver
|
4
|
-
def initialize(receiver)
|
5
|
-
self.receiver = receiver
|
6
|
-
end
|
7
|
-
|
8
|
-
def receive(&block)
|
9
|
-
receiver.receive(&block)
|
10
|
-
end
|
11
|
-
|
12
|
-
def receive_loop
|
13
|
-
receiver.receive_loop
|
14
|
-
end
|
15
|
-
|
16
|
-
def send!(*term)
|
17
|
-
receiver.send!(*term)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|