lwes 0.7.0 → 0.8.0pre1

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,6 +2,7 @@ require 'net/http'
2
2
  require 'mkmf'
3
3
  require 'fileutils'
4
4
  dir_config('lwes')
5
+ have_func('rb_thread_blocking_region', 'ruby.h')
5
6
 
6
7
  pwd = File.expand_path(File.dirname(__FILE__))
7
8
  v = '0.23.1'
@@ -41,14 +42,14 @@ end
41
42
  unless have_library('lwes') && have_header('lwes.h')
42
43
  warn "LWES library not found, building locally"
43
44
  sub_cd(pwd) do
44
- unless test ?r, tgz
45
+ unless File.readable?(tgz)
45
46
  response = fetch(url)
46
47
  File.open("#{tgz}.#{$$}.#{rand}.tmp", "wb") do |fp|
47
48
  fp.write(response.body)
48
49
  File.rename(fp.path, tgz)
49
50
  end
50
51
  end
51
- unless test ?r, "#{inst}/.ok"
52
+ unless File.readable?("#{inst}/.ok")
52
53
  FileUtils.rm_rf(dir)
53
54
  system('tar', 'zxf', tgz) or abort "tar failed with #{$?}"
54
55
  sub_cd(dir) do
@@ -0,0 +1,165 @@
1
+ #include "lwes_ruby.h"
2
+ #include <errno.h>
3
+
4
+ static void listener_free(void *ptr)
5
+ {
6
+ struct lwes_listener *listener = ptr;
7
+
8
+ if (listener)
9
+ (void)lwes_listener_destroy(listener);
10
+ }
11
+
12
+ static struct lwes_listener *listener_ptr(VALUE self)
13
+ {
14
+ struct lwes_listener *listener = DATA_PTR(self);
15
+
16
+ if (listener == NULL)
17
+ rb_raise(rb_eIOError, "closed listener");
18
+ return listener;
19
+ }
20
+
21
+ static void fail(int rc, const char *fn)
22
+ {
23
+ if (errno)
24
+ rb_sys_fail(fn);
25
+ rb_raise(rb_eRuntimeError, "%s failed with: %d", fn, rc);
26
+ }
27
+
28
+ /*
29
+ * call-seq:
30
+ * listener.close => nil
31
+ *
32
+ * Closes the socket used by the LWES::Listener object. Raises IOError if
33
+ * already closed.
34
+ */
35
+ static VALUE listener_close(VALUE self)
36
+ {
37
+ struct lwes_listener *listener = listener_ptr(self);
38
+ int err;
39
+
40
+ DATA_PTR(self) = NULL;
41
+ errno = 0;
42
+ err = lwes_listener_destroy(listener);
43
+ if (err)
44
+ fail(err, "lwes_listener_destroy()");
45
+ return Qnil;
46
+ }
47
+
48
+ static VALUE listener_alloc(VALUE klass)
49
+ {
50
+ return Data_Wrap_Struct(klass, NULL, listener_free, NULL);
51
+ }
52
+
53
+ /*
54
+ * call-seq:
55
+ * listener = LWES::Listener.new(address: "224.1.1.11", port: 12345)
56
+ *
57
+ * Binds an LWES listener socket to receive events on. It takes the following
58
+ * options:
59
+ *
60
+ * - +:address+ is a dotted quad string of an IPv4 address to listen on.
61
+ * - +:port+ is the port to listen on
62
+ * - +:iface+ is a dotted quad string of the interface to listen on (optional)
63
+ */
64
+ static VALUE listener_init(VALUE self, VALUE options)
65
+ {
66
+ struct lwes_listener *listener;
67
+ LWES_SHORT_STRING address;
68
+ LWES_SHORT_STRING iface;
69
+ LWES_U_INT_32 port;
70
+ VALUE tmp;
71
+
72
+ if (DATA_PTR(self))
73
+ rb_raise(rb_eRuntimeError,
74
+ "initializing already initialized Listener");
75
+ if (TYPE(options) != T_HASH)
76
+ rb_raise(rb_eTypeError, "options must be a hash");
77
+
78
+ tmp = rb_hash_aref(options, ID2SYM(rb_intern("address")));
79
+ address = StringValueCStr(tmp);
80
+
81
+ tmp = rb_hash_aref(options, ID2SYM(rb_intern("iface")));
82
+ iface = NIL_P(tmp) ? NULL : StringValueCStr(tmp);
83
+
84
+ tmp = rb_hash_aref(options, ID2SYM(rb_intern("port")));
85
+ port = (LWES_U_INT_32)lwesrb_uint16(tmp);
86
+
87
+ listener = lwes_listener_create(address, iface, port);
88
+ if (listener == NULL) {
89
+ rb_gc();
90
+ listener = lwes_listener_create(address, iface, port);
91
+ if (listener == NULL)
92
+ rb_raise(rb_eRuntimeError,
93
+ "failed to create LWES Listener");
94
+ }
95
+
96
+ DATA_PTR(self) = listener;
97
+
98
+ return self;
99
+ }
100
+
101
+ struct recv_args {
102
+ struct lwes_listener *listener;
103
+ struct lwes_event *event;
104
+ unsigned timeout_ms;
105
+ };
106
+
107
+ static VALUE recv_event(void *ptr)
108
+ {
109
+ struct recv_args *a = ptr;
110
+ int r;
111
+
112
+ if (a->timeout_ms == UINT_MAX)
113
+ r = lwes_listener_recv(a->listener, a->event);
114
+ else
115
+ r = lwes_listener_recv_by(a->listener, a->event, a->timeout_ms);
116
+ return (VALUE)r;
117
+ }
118
+
119
+ #ifdef HAVE_RB_THREAD_BLOCKING_REGION
120
+ /*
121
+ * call-seq:
122
+ * listener.recv => LWES::Event
123
+ * listener.recv(timeout_ms) => LWES::Event or nil
124
+ *
125
+ * Receives and returns one LWES::Event from the network.
126
+ * An optional timeout (in milliseconds) may be specified and cause this
127
+ * to return +nil+ on timeout. This method is only available under Ruby 1.9.
128
+ */
129
+ static VALUE listener_recv(int argc, VALUE *argv, VALUE self)
130
+ {
131
+ struct recv_args args;
132
+ VALUE timeout;
133
+ int r, saved_errno;
134
+
135
+ rb_scan_args(argc, argv, "01", &timeout);
136
+
137
+ args.listener = listener_ptr(self);
138
+ args.event = lwes_event_create_no_name(NULL);
139
+ args.timeout_ms = NIL_P(timeout) ? UINT_MAX : NUM2UINT(timeout);
140
+
141
+ saved_errno = errno = 0;
142
+ r = (int)rb_thread_blocking_region(recv_event, &args, RUBY_UBF_IO, 0);
143
+ if (r >= 0)
144
+ return lwesrb_wrap_event(cLWES_Event, args.event);
145
+ saved_errno = errno;
146
+ (void)lwes_event_destroy(args.event);
147
+ if (r == -2 && ! NIL_P(timeout))
148
+ return Qnil;
149
+ errno = saved_errno;
150
+ fail(r, "lwes_listener_recv(_by)");
151
+ return Qnil;
152
+ }
153
+ #endif /* HAVE_RB_THREAD_BLOCKING_REGION */
154
+
155
+ void lwesrb_init_listener(void)
156
+ {
157
+ VALUE mLWES = rb_define_module("LWES");
158
+ VALUE cListener = rb_define_class_under(mLWES, "Listener", rb_cObject);
159
+ rb_define_alloc_func(cListener, listener_alloc);
160
+ rb_define_private_method(cListener, "initialize", listener_init, 1);
161
+ #ifdef HAVE_RB_THREAD_BLOCKING_REGION
162
+ rb_define_method(cListener, "recv", listener_recv, -1);
163
+ #endif
164
+ rb_define_method(cListener, "close", listener_close, 0);
165
+ }
File without changes
@@ -23,4 +23,5 @@ void Init_lwes_ext(void)
23
23
  lwesrb_init_emitter();
24
24
  lwesrb_init_type_db();
25
25
  lwesrb_init_event();
26
+ lwesrb_init_listener();
26
27
  }
@@ -21,6 +21,10 @@ void lwesrb_init_numeric(void);
21
21
 
22
22
  void lwesrb_init_event(void);
23
23
 
24
+ void lwesrb_init_listener(void);
25
+
26
+ VALUE lwesrb_wrap_event(VALUE klass, struct lwes_event *e);
27
+
24
28
  void lwesrb_dump_type(LWES_BYTE type, LWES_BYTE_P buf, size_t *off);
25
29
 
26
30
  void lwesrb_dump_num(LWES_BYTE type, VALUE val, LWES_BYTE_P buf, size_t *off);
@@ -37,17 +41,20 @@ void lwesrb_dump_num_ary(VALUE array, LWES_BYTE_P buf, size_t *off);
37
41
  # define RARRAY_LEN(s) (RARRAY(s)->len)
38
42
  #endif
39
43
 
40
- #ifndef RSTRUCT_PTR
41
- # define RSTRUCT_PTR(s) (RSTRUCT(s)->ptr)
42
- # define RSTRUCT_LEN(s) (RSTRUCT(s)->len)
43
- #endif
44
-
45
44
  #define RAISE_INSPECT(v) RSTRING_PTR(raise_inspect = rb_inspect(v))
46
45
 
47
46
  extern VALUE cLWES_Event;
48
47
 
49
48
  struct lwes_event * lwesrb_get_event(VALUE self);
50
49
 
51
- VALUE lwesrb_event_to_hash(struct lwes_event *e);
50
+ LWES_U_INT_16 lwesrb_uint16(VALUE val);
51
+ LWES_INT_16 lwesrb_int16(VALUE val);
52
+ LWES_U_INT_32 lwesrb_uint32(VALUE val);
53
+ LWES_INT_32 lwesrb_int32(VALUE val);
54
+ LWES_U_INT_64 lwesrb_uint64(VALUE val);
55
+ /* NUM2LL can raise RangeError */
56
+ #define lwesrb_int64(VAL) (LWES_INT_64)NUM2LL((VAL))
57
+ LWES_IP_ADDR lwesrb_ip_addr(VALUE val);
58
+ LWES_BOOLEAN lwesrb_boolean(VALUE val);
52
59
 
53
60
  #endif /* LWES_RUBY_H */
@@ -8,110 +8,46 @@ static ID
8
8
  sym_ip_addr;
9
9
  static ID id_to_i;
10
10
 
11
- void lwesrb_dump_type(LWES_BYTE type, LWES_BYTE_P buf, size_t *off)
12
- {
13
- if (marshall_BYTE(type, buf, MAX_MSG_SIZE, off) > 0)
14
- return;
15
- rb_raise(rb_eRuntimeError, "failed to dump type=%02x", (unsigned)type);
16
- }
17
-
18
11
  static int dump_uint16(VALUE val, LWES_BYTE_P buf, size_t *off)
19
12
  {
20
- int32_t tmp = NUM2INT(val);
21
-
22
- if (tmp < 0)
23
- rb_raise(rb_eRangeError, ":uint16 negative: %d", tmp);
24
- if (tmp > UINT16_MAX)
25
- rb_raise(rb_eRangeError, ":uint16 too large: %d", tmp);
26
-
27
13
  lwesrb_dump_type(LWES_U_INT_16_TOKEN, buf, off);
28
- return marshall_U_INT_16((LWES_U_INT_16)tmp, buf, MAX_MSG_SIZE, off);
14
+ return marshall_U_INT_16(lwesrb_uint16(val), buf, MAX_MSG_SIZE, off);
29
15
  }
30
16
 
31
17
  static int dump_int16(VALUE val, LWES_BYTE_P buf, size_t *off)
32
18
  {
33
- int32_t tmp = NUM2INT(val);
34
-
35
- if (tmp > INT16_MAX)
36
- rb_raise(rb_eRangeError, ":int16 too large: %i", tmp);
37
- if (tmp < INT16_MIN)
38
- rb_raise(rb_eRangeError, ":int16 too small: %i", tmp);
39
-
40
19
  lwesrb_dump_type(LWES_INT_16_TOKEN, buf, off);
41
- return marshall_INT_16((LWES_INT_16)tmp, buf, MAX_MSG_SIZE, off);
20
+ return marshall_INT_16(lwesrb_int16(val), buf, MAX_MSG_SIZE, off);
42
21
  }
43
22
 
44
23
  static int dump_uint32(VALUE val, LWES_BYTE_P buf, size_t *off)
45
24
  {
46
- LONG_LONG tmp = NUM2LL(val);
47
-
48
- if (tmp < 0)
49
- rb_raise(rb_eRangeError, ":uint32 negative: %lli", tmp);
50
- if (tmp > UINT32_MAX)
51
- rb_raise(rb_eRangeError, ":uint32 too large: %lli", tmp);
52
-
53
25
  lwesrb_dump_type(LWES_U_INT_32_TOKEN, buf, off);
54
- return marshall_U_INT_32((LWES_U_INT_32)tmp, buf, MAX_MSG_SIZE, off);
26
+ return marshall_U_INT_32(lwesrb_uint32(val), buf, MAX_MSG_SIZE, off);
55
27
  }
56
28
 
57
29
  static int dump_int32(VALUE val, LWES_BYTE_P buf, size_t *off)
58
30
  {
59
- LONG_LONG tmp = NUM2LL(val);
60
-
61
- if (tmp > INT32_MAX)
62
- rb_raise(rb_eRangeError, ":int32 too large: %lli", tmp);
63
- if (tmp < INT32_MIN)
64
- rb_raise(rb_eRangeError, ":int32 too small: %lli", tmp);
65
-
66
31
  lwesrb_dump_type(LWES_INT_32_TOKEN, buf, off);
67
- return marshall_INT_32((LWES_INT_32)tmp, buf, MAX_MSG_SIZE, off);
32
+ return marshall_INT_32(lwesrb_int32(val), buf, MAX_MSG_SIZE, off);
68
33
  }
69
34
 
70
35
  static int dump_uint64(VALUE val, LWES_BYTE_P buf, size_t *off)
71
36
  {
72
- unsigned LONG_LONG tmp = NUM2ULL(val); /* can raise RangeError */
73
- ID type = TYPE(val);
74
-
75
- if ((type == T_FIXNUM && FIX2LONG(val) < 0) ||
76
- (type == T_BIGNUM && RTEST(rb_funcall(val, '<', 1, INT2FIX(0))))) {
77
- volatile VALUE raise_inspect;
78
-
79
- rb_raise(rb_eRangeError, ":uint64 negative: %s",
80
- RAISE_INSPECT(val));
81
- }
82
-
83
37
  lwesrb_dump_type(LWES_U_INT_64_TOKEN, buf, off);
84
- return marshall_U_INT_64((LWES_U_INT_64)tmp, buf, MAX_MSG_SIZE, off);
38
+ return marshall_U_INT_64(lwesrb_uint64(val), buf, MAX_MSG_SIZE, off);
85
39
  }
86
40
 
87
41
  static int dump_int64(VALUE val, LWES_BYTE_P buf, size_t *off)
88
42
  {
89
- LONG_LONG tmp = NUM2LL(val); /* can raise RangeError */
90
-
91
43
  lwesrb_dump_type(LWES_INT_64_TOKEN, buf, off);
92
- return marshall_INT_64((LWES_INT_64)tmp, buf, MAX_MSG_SIZE, off);
44
+ return marshall_INT_64(lwesrb_int64(val), buf, MAX_MSG_SIZE, off);
93
45
  }
94
46
 
95
47
  static int dump_ip_addr(VALUE val, LWES_BYTE_P buf, size_t *off)
96
48
  {
97
- LWES_IP_ADDR addr;
98
- volatile VALUE raise_inspect;
99
-
100
- switch (TYPE(val)) {
101
- case T_STRING:
102
- addr.s_addr = inet_addr(RSTRING_PTR(val));
103
- break;
104
- case T_FIXNUM:
105
- case T_BIGNUM:
106
- addr.s_addr = htonl(NUM2UINT(val));
107
- break;
108
- default:
109
- rb_raise(rb_eTypeError,
110
- ":ip_addr address must be String or Integer: %s",
111
- RAISE_INSPECT(val));
112
- }
113
49
  lwesrb_dump_type(LWES_IP_ADDR_TOKEN, buf, off);
114
- return marshall_IP_ADDR(addr, buf, MAX_MSG_SIZE, off);
50
+ return marshall_IP_ADDR(lwesrb_ip_addr(val), buf, MAX_MSG_SIZE, off);
115
51
  }
116
52
 
117
53
  /* simple type => function dispatch map */
@@ -0,0 +1,99 @@
1
+ #include "lwes_ruby.h"
2
+
3
+ void lwesrb_dump_type(LWES_BYTE type, LWES_BYTE_P buf, size_t *off)
4
+ {
5
+ if (marshall_BYTE(type, buf, MAX_MSG_SIZE, off) > 0)
6
+ return;
7
+ rb_raise(rb_eRuntimeError, "failed to dump type=%02x", (unsigned)type);
8
+ }
9
+
10
+ LWES_U_INT_16 lwesrb_uint16(VALUE val)
11
+ {
12
+ int32_t tmp = NUM2INT(val);
13
+
14
+ if (tmp != (LWES_U_INT_16)tmp) {
15
+ const char *s = tmp < 0 ? "negative" : "too big";
16
+ rb_raise(rb_eRangeError, ":uint16 %s: %d", s, tmp);
17
+ }
18
+ return (LWES_U_INT_16)tmp;
19
+ }
20
+
21
+ LWES_INT_16 lwesrb_int16(VALUE val)
22
+ {
23
+ int32_t tmp = NUM2INT(val);
24
+
25
+ if (tmp != (LWES_INT_16)tmp) {
26
+ const char *s = tmp < 0 ? "small" : "big";
27
+ rb_raise(rb_eRangeError, ":int16 too %s: %d", s, tmp);
28
+ }
29
+ return (LWES_INT_16)tmp;
30
+ }
31
+
32
+ LWES_U_INT_32 lwesrb_uint32(VALUE val)
33
+ {
34
+ LONG_LONG tmp = NUM2LL(val);
35
+
36
+ if (tmp != (LWES_U_INT_32)tmp) {
37
+ const char *s = tmp < 0 ? "negative" : "too big";
38
+ rb_raise(rb_eRangeError, ":uint32 %s: %lli", s, tmp);
39
+ }
40
+ return (LWES_U_INT_32)tmp;
41
+ }
42
+
43
+ LWES_INT_32 lwesrb_int32(VALUE val)
44
+ {
45
+ LONG_LONG tmp = NUM2LL(val);
46
+
47
+ if (tmp != (LWES_INT_32)tmp) {
48
+ const char *s = tmp < 0 ? "small" : "big";
49
+ rb_raise(rb_eRangeError, ":int32 too %s: %lli", s, tmp);
50
+ }
51
+ return (LWES_INT_32)tmp;
52
+ }
53
+
54
+ LWES_U_INT_64 lwesrb_uint64(VALUE val)
55
+ {
56
+ unsigned LONG_LONG tmp = NUM2ULL(val); /* can raise RangeError */
57
+ ID type = TYPE(val);
58
+
59
+ if ((type == T_FIXNUM && FIX2LONG(val) < 0) ||
60
+ (type == T_BIGNUM && RTEST(rb_funcall(val, '<', 1, INT2FIX(0))))) {
61
+ volatile VALUE raise_inspect;
62
+
63
+ rb_raise(rb_eRangeError, ":uint64 negative: %s",
64
+ RAISE_INSPECT(val));
65
+ }
66
+ return (LWES_U_INT_64)tmp;
67
+ }
68
+
69
+ LWES_IP_ADDR lwesrb_ip_addr(VALUE val)
70
+ {
71
+ LWES_IP_ADDR addr;
72
+ volatile VALUE raise_inspect;
73
+
74
+ switch (TYPE(val)) {
75
+ case T_STRING:
76
+ addr.s_addr = inet_addr(StringValueCStr(val));
77
+ break;
78
+ case T_FIXNUM:
79
+ case T_BIGNUM:
80
+ addr.s_addr = htonl(NUM2UINT(val));
81
+ break;
82
+ default:
83
+ rb_raise(rb_eTypeError,
84
+ ":ip_addr address must be String or Integer: %s",
85
+ RAISE_INSPECT(val));
86
+ }
87
+
88
+ return addr;
89
+ }
90
+
91
+ LWES_BOOLEAN lwesrb_boolean(VALUE val)
92
+ {
93
+ if (val == Qtrue) return TRUE;
94
+ if (val != Qfalse) {
95
+ volatile VALUE raise_inspect;
96
+ rb_raise(rb_eTypeError, "non-boolean: %s", RAISE_INSPECT(val));
97
+ }
98
+ return FALSE;
99
+ }
@@ -2,48 +2,38 @@
2
2
 
3
3
  VALUE cLWES_TypeDB;
4
4
 
5
- struct _tdb {
6
- struct lwes_event_type_db *db;
7
- };
8
-
9
5
  static void tdb_free(void *ptr)
10
6
  {
11
- struct _tdb *tdb = ptr;
7
+ struct lwes_event_type_db *db = ptr;
12
8
 
13
- if (tdb->db)
14
- lwes_event_type_db_destroy(tdb->db);
15
- xfree(ptr);
9
+ if (db)
10
+ lwes_event_type_db_destroy(db);
16
11
  }
17
12
 
18
13
  static VALUE tdb_alloc(VALUE klass)
19
14
  {
20
- struct _tdb *tdb;
21
-
22
- return Data_Make_Struct(klass, struct _tdb, NULL, tdb_free, tdb);
15
+ return Data_Wrap_Struct(klass, NULL, tdb_free, NULL);
23
16
  }
24
17
 
25
18
  static VALUE tdb_init(VALUE self, VALUE path)
26
19
  {
27
- struct _tdb *tdb;
20
+ struct lwes_event_type_db *db = DATA_PTR(self);
28
21
  int gc_retry = 1;
22
+ char *cpath = StringValueCStr(path);
29
23
 
30
- if (TYPE(path) != T_STRING)
31
- rb_raise(rb_eArgError, "path must be a string");
32
-
33
- Data_Get_Struct(self, struct _tdb, tdb);
34
- if (tdb->db)
24
+ if (db)
35
25
  rb_raise(rb_eRuntimeError, "ESF already initialized");
36
26
  retry:
37
- tdb->db = lwes_event_type_db_create(RSTRING_PTR(path));
38
- if (!tdb->db) {
27
+ db = lwes_event_type_db_create(cpath);
28
+ if (!db) {
39
29
  if (--gc_retry == 0) {
40
30
  rb_gc();
41
31
  goto retry;
42
32
  }
43
33
  rb_raise(rb_eRuntimeError,
44
- "failed to create type DB for LWES file %s",
45
- RSTRING_PTR(path));
34
+ "failed to create type DB for LWES file %s", cpath);
46
35
  }
36
+ DATA_PTR(self) = db;
47
37
 
48
38
  return Qnil;
49
39
  }
@@ -100,28 +90,26 @@ static VALUE event_def_ary(struct lwes_hash *hash)
100
90
 
101
91
  struct lwes_event_type_db * lwesrb_get_type_db(VALUE self)
102
92
  {
103
- struct _tdb *tdb;
93
+ struct lwes_event_type_db *db = DATA_PTR(self);
104
94
 
105
- Data_Get_Struct(self, struct _tdb, tdb);
106
- if (! tdb->db) {
95
+ if (!db) {
107
96
  volatile VALUE raise_inspect;
108
97
  rb_raise(rb_eRuntimeError,
109
98
  "couldn't get lwes_type_db from %s",
110
99
  RAISE_INSPECT(self));
111
100
  }
112
- return tdb->db;
101
+ return db;
113
102
  }
114
103
 
115
104
  static VALUE tdb_to_hash(VALUE self)
116
105
  {
117
- struct _tdb *tdb;
106
+ struct lwes_event_type_db *db = DATA_PTR(self);
118
107
  VALUE rv = rb_hash_new();
119
108
  struct lwes_hash *events;
120
109
  struct lwes_hash_enumeration e;
121
110
 
122
- Data_Get_Struct(self, struct _tdb, tdb);
123
- assert(tdb->db && tdb->db->events && "tdb not initialized");
124
- events = tdb->db->events;
111
+ assert(db && db->events && "tdb not initialized");
112
+ events = db->events;
125
113
 
126
114
  if (!lwes_hash_keys(events, &e))
127
115
  rb_raise(rb_eRuntimeError, "couldn't get type_db events");
@@ -151,7 +139,7 @@ void lwesrb_init_type_db(void)
151
139
  {
152
140
  VALUE mLWES = rb_define_module("LWES");
153
141
  cLWES_TypeDB = rb_define_class_under(mLWES, "TypeDB", rb_cObject);
154
- rb_define_method(cLWES_TypeDB, "initialize", tdb_init, 1);
142
+ rb_define_private_method(cLWES_TypeDB, "initialize", tdb_init, 1);
155
143
  rb_define_method(cLWES_TypeDB, "to_hash", tdb_to_hash, 0);
156
144
  rb_define_alloc_func(cLWES_TypeDB, tdb_alloc);
157
145
  }
@@ -0,0 +1,50 @@
1
+ # :stopdoc:
2
+ # internal class
3
+ module LWES::ClassMaker
4
+ def type_db(options)
5
+ options[:db] || begin
6
+ file = options[:file] or
7
+ raise ArgumentError, "TypeDB :db or ESF :file missing"
8
+ File.readable?(file) or
9
+ raise ArgumentError, "file #{file.inspect} not readable"
10
+ LWES::TypeDB.new(file)
11
+ end
12
+ end
13
+
14
+ def class_for(options, dump)
15
+ klass = options[:class] || begin
16
+ # make it easier to deal with single event files
17
+ events = (dump.keys - [ :MetaEventInfo ])
18
+ events.size > 1 and
19
+ raise RuntimeError,
20
+ "multiple event defs available: #{events.inspect}\n" \
21
+ "pick one with :class"
22
+ events.first
23
+ end
24
+ name = options[:name] || klass.to_s
25
+ event_def = dump[name.to_sym] or
26
+ raise RuntimeError, "#{name.inspect} not defined in #{file}"
27
+ [ klass, name, event_def ]
28
+ end
29
+
30
+ def set_constants(tmp, db, klass, name, options)
31
+ tmp.const_set :TYPE_DB, db
32
+ tmp.const_set :NAME, name.to_s.dup.freeze
33
+ defaults = options[:defaults] || {}
34
+ tmp.const_set :DEFAULTS, defaults.dup
35
+ parent = options.include?(:parent) ? options[:parent] : Object
36
+ parent or return
37
+ components = klass.to_s.split(/::/)
38
+ components.each_with_index do |component, i|
39
+ if i == (components.size - 1)
40
+ tmp = parent.const_set(component, tmp)
41
+ else
42
+ parent = begin
43
+ parent.const_get(component)
44
+ rescue NameError
45
+ parent.const_set component, Module.new
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
data/lib/lwes/event.rb CHANGED
@@ -1,7 +1,57 @@
1
- # this class is incomplete, but will eventually be subclassable
2
- # like Struct in Ruby
1
+ # Used for mapping LWES events in in an ESF file to a Ruby object.
2
+ # LWES::Event-derived classes are more memory efficient if your event
3
+ # definitions have many unused fields.
4
+ #
5
+ # LWES::TypeDB.create_classes! with +:sparse+ set to +true+
3
6
  class LWES::Event
7
+ SYM2ATTR = Hash.new { |h,k| h[k] = k.to_s.freeze } # :nodoc:
8
+
9
+ # used to cache classes for LWES::Event.parse
10
+ CLASSES = {} # :nodoc:
11
+ extend LWES::ClassMaker
12
+
13
+ def self.subclass(options, &block)
14
+ db = type_db(options)
15
+ dump = db.to_hash
16
+ klass, name, event_def = class_for(options, dump)
17
+ tmp = Class.new(self)
18
+ set_constants(tmp, db, klass, name, options)
19
+ tmp.class_eval(&block) if block_given?
20
+
21
+ meta = dump[:MetaEventInfo] || []
22
+ methods = meta + event_def
23
+ methods = methods.inject("") do |str, (k,_)|
24
+ str << "def #{k}; self[:#{k}]; end\n"
25
+ str << "def #{k}= val; self[:#{k}] = val; end\n"
26
+ end
27
+ methods << "def initialize(src = nil); merge!(DEFAULTS); super; end\n"
28
+ tmp.class_eval methods
29
+ CLASSES[name] = tmp
30
+ end
31
+
4
32
  def inspect
5
- "#<#{self.class}:#{to_hash}>"
33
+ klass = self.class
34
+ if LWES::Event == klass
35
+ "#<#{klass}:#{to_hash.inspect}>"
36
+ else
37
+ "#<#{klass}(event:#{klass.const_get(:NAME)}):#{to_hash.inspect}>"
38
+ end
39
+ end
40
+
41
+ def merge! src
42
+ src.to_hash.each { |k,v| self[k] = v }
43
+ self
44
+ end
45
+
46
+ alias_method :initialize_copy, :merge!
47
+
48
+ def merge src
49
+ dup.merge! src
50
+ end
51
+
52
+ private
53
+
54
+ def initialize(src = nil)
55
+ src and merge! src
6
56
  end
7
57
  end
@@ -0,0 +1,26 @@
1
+ #
2
+ # This class is only supported on Ruby 1.9
3
+ #
4
+ # listener = LWES::Listener.new :address => "224.1.1.11", :port => 12345
5
+ # listener.each do |event|
6
+ # p event
7
+ # end
8
+ #
9
+ class LWES::Listener
10
+
11
+ # we disallow dup-ing objects since GC could double-free otherwise
12
+ def dup
13
+ self
14
+ end
15
+
16
+ alias clone dup
17
+
18
+ # process each LWES::Event object as it is received
19
+ def each
20
+ begin
21
+ yield recv
22
+ rescue Errno::EINTR
23
+ end while true
24
+ self
25
+ end
26
+ end