iodine 0.4.19 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of iodine might be problematic. Click here for more details.

Files changed (146) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -2
  3. data/CHANGELOG.md +22 -0
  4. data/LIMITS.md +19 -9
  5. data/README.md +92 -77
  6. data/SPEC-PubSub-Draft.md +113 -0
  7. data/SPEC-Websocket-Draft.md +127 -143
  8. data/bin/http-hello +0 -1
  9. data/bin/raw-rbhttp +1 -1
  10. data/bin/raw_broadcast +8 -10
  11. data/bin/updated api +2 -2
  12. data/bin/ws-broadcast +2 -4
  13. data/bin/ws-echo +2 -2
  14. data/examples/config.ru +13 -13
  15. data/examples/echo.ru +5 -6
  16. data/examples/hello.ru +2 -3
  17. data/examples/info.md +316 -0
  18. data/examples/pubsub_engine.ru +81 -0
  19. data/examples/redis.ru +9 -9
  20. data/examples/shootout.ru +45 -11
  21. data/ext/iodine/defer.c +194 -297
  22. data/ext/iodine/defer.h +61 -53
  23. data/ext/iodine/evio.c +0 -260
  24. data/ext/iodine/evio.h +50 -22
  25. data/ext/iodine/evio_callbacks.c +26 -0
  26. data/ext/iodine/evio_epoll.c +251 -0
  27. data/ext/iodine/evio_kqueue.c +193 -0
  28. data/ext/iodine/extconf.rb +1 -1
  29. data/ext/iodine/facil.c +1420 -542
  30. data/ext/iodine/facil.h +151 -64
  31. data/ext/iodine/fio_ary.h +418 -0
  32. data/ext/iodine/{base64.c → fio_base64.c} +33 -24
  33. data/ext/iodine/{base64.h → fio_base64.h} +6 -7
  34. data/ext/iodine/{fio_cli_helper.c → fio_cli.c} +77 -58
  35. data/ext/iodine/{fio_cli_helper.h → fio_cli.h} +9 -4
  36. data/ext/iodine/fio_hashmap.h +759 -0
  37. data/ext/iodine/fio_json_parser.h +651 -0
  38. data/ext/iodine/fio_llist.h +257 -0
  39. data/ext/iodine/fio_mem.c +672 -0
  40. data/ext/iodine/fio_mem.h +140 -0
  41. data/ext/iodine/fio_random.c +248 -0
  42. data/ext/iodine/{random.h → fio_random.h} +11 -14
  43. data/ext/iodine/{sha1.c → fio_sha1.c} +28 -24
  44. data/ext/iodine/{sha1.h → fio_sha1.h} +38 -16
  45. data/ext/iodine/{sha2.c → fio_sha2.c} +66 -49
  46. data/ext/iodine/{sha2.h → fio_sha2.h} +57 -26
  47. data/ext/iodine/{fiobj_internal.c → fio_siphash.c} +9 -90
  48. data/ext/iodine/fio_siphash.h +18 -0
  49. data/ext/iodine/fio_tmpfile.h +38 -0
  50. data/ext/iodine/fiobj.h +24 -7
  51. data/ext/iodine/fiobj4sock.h +23 -0
  52. data/ext/iodine/fiobj_ary.c +143 -226
  53. data/ext/iodine/fiobj_ary.h +17 -16
  54. data/ext/iodine/fiobj_data.c +1160 -0
  55. data/ext/iodine/fiobj_data.h +164 -0
  56. data/ext/iodine/fiobj_hash.c +298 -406
  57. data/ext/iodine/fiobj_hash.h +101 -54
  58. data/ext/iodine/fiobj_json.c +478 -601
  59. data/ext/iodine/fiobj_json.h +34 -9
  60. data/ext/iodine/fiobj_numbers.c +383 -51
  61. data/ext/iodine/fiobj_numbers.h +87 -11
  62. data/ext/iodine/fiobj_str.c +423 -184
  63. data/ext/iodine/fiobj_str.h +81 -32
  64. data/ext/iodine/fiobject.c +273 -522
  65. data/ext/iodine/fiobject.h +477 -112
  66. data/ext/iodine/http.c +2243 -83
  67. data/ext/iodine/http.h +842 -121
  68. data/ext/iodine/http1.c +810 -385
  69. data/ext/iodine/http1.h +16 -39
  70. data/ext/iodine/http1_parser.c +146 -74
  71. data/ext/iodine/http1_parser.h +15 -4
  72. data/ext/iodine/http_internal.c +1258 -0
  73. data/ext/iodine/http_internal.h +226 -0
  74. data/ext/iodine/http_mime_parser.h +341 -0
  75. data/ext/iodine/iodine.c +86 -68
  76. data/ext/iodine/iodine.h +26 -11
  77. data/ext/iodine/iodine_helpers.c +8 -7
  78. data/ext/iodine/iodine_http.c +487 -324
  79. data/ext/iodine/iodine_json.c +304 -0
  80. data/ext/iodine/iodine_json.h +6 -0
  81. data/ext/iodine/iodine_protocol.c +107 -45
  82. data/ext/iodine/iodine_pubsub.c +526 -225
  83. data/ext/iodine/iodine_pubsub.h +10 -0
  84. data/ext/iodine/iodine_websockets.c +268 -510
  85. data/ext/iodine/iodine_websockets.h +2 -4
  86. data/ext/iodine/pubsub.c +726 -432
  87. data/ext/iodine/pubsub.h +85 -103
  88. data/ext/iodine/rb-call.c +4 -4
  89. data/ext/iodine/rb-defer.c +46 -22
  90. data/ext/iodine/rb-fiobj2rb.h +117 -0
  91. data/ext/iodine/rb-rack-io.c +73 -238
  92. data/ext/iodine/rb-rack-io.h +2 -2
  93. data/ext/iodine/rb-registry.c +35 -93
  94. data/ext/iodine/rb-registry.h +1 -0
  95. data/ext/iodine/redis_engine.c +742 -304
  96. data/ext/iodine/redis_engine.h +42 -39
  97. data/ext/iodine/resp_parser.h +311 -0
  98. data/ext/iodine/sock.c +627 -490
  99. data/ext/iodine/sock.h +345 -297
  100. data/ext/iodine/spnlock.inc +15 -4
  101. data/ext/iodine/websocket_parser.h +16 -20
  102. data/ext/iodine/websockets.c +188 -257
  103. data/ext/iodine/websockets.h +24 -133
  104. data/lib/iodine.rb +52 -7
  105. data/lib/iodine/cli.rb +6 -24
  106. data/lib/iodine/json.rb +40 -0
  107. data/lib/iodine/version.rb +1 -1
  108. data/lib/iodine/websocket.rb +5 -3
  109. data/lib/rack/handler/iodine.rb +58 -13
  110. metadata +38 -48
  111. data/bin/ws-shootout +0 -107
  112. data/examples/broadcast.ru +0 -56
  113. data/ext/iodine/bscrypt-common.h +0 -116
  114. data/ext/iodine/bscrypt.h +0 -49
  115. data/ext/iodine/fio2resp.c +0 -60
  116. data/ext/iodine/fio2resp.h +0 -51
  117. data/ext/iodine/fio_dict.c +0 -446
  118. data/ext/iodine/fio_dict.h +0 -99
  119. data/ext/iodine/fio_hash_table.h +0 -370
  120. data/ext/iodine/fio_list.h +0 -111
  121. data/ext/iodine/fiobj_internal.h +0 -280
  122. data/ext/iodine/fiobj_primitives.c +0 -131
  123. data/ext/iodine/fiobj_primitives.h +0 -55
  124. data/ext/iodine/fiobj_sym.c +0 -135
  125. data/ext/iodine/fiobj_sym.h +0 -60
  126. data/ext/iodine/hex.c +0 -124
  127. data/ext/iodine/hex.h +0 -70
  128. data/ext/iodine/http1_request.c +0 -81
  129. data/ext/iodine/http1_request.h +0 -58
  130. data/ext/iodine/http1_response.c +0 -417
  131. data/ext/iodine/http1_response.h +0 -95
  132. data/ext/iodine/http_request.c +0 -111
  133. data/ext/iodine/http_request.h +0 -102
  134. data/ext/iodine/http_response.c +0 -1703
  135. data/ext/iodine/http_response.h +0 -250
  136. data/ext/iodine/misc.c +0 -182
  137. data/ext/iodine/misc.h +0 -74
  138. data/ext/iodine/random.c +0 -208
  139. data/ext/iodine/redis_connection.c +0 -278
  140. data/ext/iodine/redis_connection.h +0 -86
  141. data/ext/iodine/resp.c +0 -842
  142. data/ext/iodine/resp.h +0 -261
  143. data/ext/iodine/siphash.c +0 -154
  144. data/ext/iodine/siphash.h +0 -22
  145. data/ext/iodine/xor-crypt.c +0 -193
  146. data/ext/iodine/xor-crypt.h +0 -107
@@ -8,20 +8,31 @@ Feel free to copy, use and enjoy according to the license provided.
8
8
  #include "rb-call.h"
9
9
 
10
10
  #include "pubsub.h"
11
+ #include "rb-fiobj2rb.h"
11
12
  #include "redis_engine.h"
13
+ #include "websockets.h"
12
14
 
13
15
  VALUE IodineEngine;
16
+ ID iodine_engine_pubid;
14
17
 
15
18
  static VALUE IodinePubSub;
19
+ static VALUE IodinePubSubSubscription;
16
20
  static ID engine_varid;
17
21
  static ID engine_subid;
18
- static ID engine_pubid;
19
22
  static ID engine_unsubid;
20
23
  static ID default_pubsubid;
21
24
 
22
- static VALUE channel_var_id;
23
- static VALUE pattern_var_id;
24
- static VALUE message_var_id;
25
+ static ID to_str_shadow_id;
26
+
27
+ static VALUE as_sym_id;
28
+ static VALUE binary_sym_id;
29
+ static VALUE handler_sym_id;
30
+ static VALUE match_sym_id;
31
+ static VALUE message_sym_id;
32
+ static VALUE redis_sym_id;
33
+ static VALUE text_sym_id;
34
+ static VALUE to_sym_id;
35
+ static VALUE channel_sym_id;
25
36
 
26
37
  /* *****************************************************************************
27
38
  Mock Functions
@@ -52,66 +63,188 @@ call this function from your own code / application.
52
63
 
53
64
  The function should return `true` on success and `nil` or `false` on failure.
54
65
  */
55
- static VALUE engine_pub_placeholder(VALUE self, VALUE channel, VALUE msg,
56
- VALUE use_pattern) {
66
+ static VALUE engine_pub_placeholder(VALUE self, VALUE channel, VALUE msg) {
67
+ { /* test for built-in C engines */
68
+ iodine_engine_s *engine;
69
+ Data_Get_Struct(self, iodine_engine_s, engine);
70
+ if (engine->p != &engine->engine) {
71
+ FIOBJ ch = fiobj_str_new(RSTRING_PTR(channel), RSTRING_LEN(channel));
72
+ FIOBJ m = fiobj_str_new(RSTRING_PTR(msg), RSTRING_LEN(msg));
73
+ pubsub_publish(.engine = engine->p, .channel = ch, .message = m);
74
+ fiobj_free(ch);
75
+ fiobj_free(msg);
76
+ return Qtrue;
77
+ }
78
+ }
57
79
  return Qnil;
58
80
  (void)self;
59
81
  (void)msg;
60
82
  (void)channel;
61
- (void)use_pattern;
62
83
  }
63
84
 
64
85
  /* *****************************************************************************
65
- Ruby API
86
+ Engine registration and resetting
66
87
  ***************************************************************************** */
67
88
 
68
- /** @!visibility public
69
- Called by the engine to distribute a `message` to a `channel`. Supports
70
- `pattern` channel matching as well.
71
-
72
- i.e.
73
-
74
- # Regular message distribution
75
- self.distribute "My Channel", "Hello!"
76
- # Pattern message distribution
77
- self.distribute "My Ch*", "Hello!", true
89
+ /**
90
+ This method adds the engine to the pub/sub system, allowing it to recieve system
91
+ wide notifications.
92
+ */
93
+ static VALUE iodine_engine_register2(VALUE self, VALUE engine) {
94
+ iodine_engine_s *e;
95
+ Registry.add(engine);
96
+ Data_Get_Struct(engine, iodine_engine_s, e);
97
+ if (e->p) {
98
+ e->handler = engine;
99
+ pubsub_engine_register(e->p);
100
+ return Qtrue;
101
+ }
102
+ return Qfalse;
103
+ (void)self;
104
+ (void)engine;
105
+ }
78
106
 
79
- Returns `self`, always.
107
+ /**
108
+ This method adds the engine to the pub/sub system, allowing it to recieve system
109
+ wide notifications.
110
+ */
111
+ static VALUE iodine_engine_register(VALUE self) {
112
+ return iodine_engine_register2(self, self);
113
+ }
80
114
 
81
- This is the ONLY method inherited from {Iodine::PubSub::Engine} that
82
- should be called from within your code (by the engine itself).
115
+ /**
116
+ This method removes the engine from the pub/sub system.
117
+ */
118
+ static VALUE iodine_engine_deregister2(VALUE self, VALUE engine) {
119
+ iodine_engine_s *e;
120
+ Data_Get_Struct(engine, iodine_engine_s, e);
121
+ if (e->p) {
122
+ pubsub_engine_deregister(e->p);
123
+ Registry.remove(engine);
124
+ return Qtrue;
125
+ }
126
+ Registry.remove(engine);
127
+ return Qfalse;
128
+ (void)self;
129
+ (void)engine;
130
+ }
83
131
 
84
- **Notice:**
132
+ /**
133
+ This method removes the engine from the pub/sub system.
134
+ */
135
+ static VALUE iodine_engine_deregister(VALUE self) {
136
+ return iodine_engine_deregister2(self, self);
137
+ }
85
138
 
86
- Message distribution requires both the {Iodine::PubSub::Engine} instance and the
87
- channel to be the same.
139
+ /**
140
+ This method resets the engine, (re)sending all the current subscription data as
141
+ if the {register} method was just called.
142
+ */
143
+ static VALUE iodine_engine_reset2(VALUE self, VALUE engine) {
144
+ iodine_engine_s *e;
145
+ Data_Get_Struct(engine, iodine_engine_s, e);
146
+ if (e->p) {
147
+ e->handler = engine;
148
+ pubsub_engine_resubscribe(e->p);
149
+ return Qtrue;
150
+ }
151
+ return Qfalse;
152
+ (void)self;
153
+ (void)engine;
154
+ }
88
155
 
89
- If a client subscribed to "channel 1" on engine A, they will NOT receive
90
- messages from "channel 1" on engine B.
156
+ /**
157
+ This method resets the engine, (re)sending all the current subscription data as
158
+ if the {register} method was just called.
91
159
  */
92
- static VALUE engine_distribute(int argc, VALUE *argv, VALUE self) {
93
- if (argc < 2 || argc > 3)
94
- rb_raise(rb_eArgError,
95
- "wrong number of arguments (given %d, expected 2..3).", argc);
96
- VALUE channel = argv[0];
97
- VALUE msg = argv[1];
98
- VALUE pattern = argc >= 3 ? argv[2] : Qnil;
99
- Check_Type(channel, T_STRING);
100
- Check_Type(msg, T_STRING);
160
+ static VALUE iodine_engine_reset(VALUE self) {
161
+ return iodine_engine_reset2(self, self);
162
+ }
101
163
 
102
- iodine_engine_s *engine;
103
- Data_Get_Struct(self, iodine_engine_s, engine);
164
+ /* *****************************************************************************
165
+ Ruby Subscription Object
166
+ ***************************************************************************** */
167
+ typedef struct {
168
+ uintptr_t subscription;
169
+ intptr_t uuid;
170
+ void *owner;
171
+ iodine_pubsub_type_e type;
172
+ } iodine_subscription_s;
173
+
174
+ static inline iodine_subscription_s subscription_data(VALUE self) {
175
+ iodine_subscription_s data = {.uuid = iodine_get_fd(self)};
176
+ if (data.uuid && !sock_isvalid(data.uuid)) {
177
+ iodine_set_fd(self, -1);
178
+ data.uuid = -1;
179
+ return data;
180
+ }
181
+
182
+ data.subscription =
183
+ ((uintptr_t)NUM2LL(rb_ivar_get(self, iodine_timeout_var_id)));
184
+ data.owner = iodine_get_cdata(self);
185
+ if (!data.owner) {
186
+ data.type = IODINE_PUBSUB_GLOBAL;
187
+ } else if ((uintptr_t)data.owner & 1) {
188
+ data.owner = (void *)((uintptr_t)data.owner & (~(uintptr_t)1));
189
+ data.type = IODINE_PUBSUB_SSE;
190
+ } else {
191
+ data.type = IODINE_PUBSUB_WEBSOCKET;
192
+ }
193
+ return data;
194
+ }
104
195
 
105
- pubsub_engine_distribute(.engine = engine->p,
106
- .channel.name = RSTRING_PTR(channel),
107
- .channel.len = RSTRING_LEN(channel),
108
- .msg.data = RSTRING_PTR(msg),
109
- .msg.len = RSTRING_LEN(msg),
110
- .use_pattern =
111
- (pattern != Qnil && pattern != Qfalse));
196
+ static inline VALUE subscription_initialize(uintptr_t sub, intptr_t uuid,
197
+ void *owner,
198
+ iodine_pubsub_type_e type,
199
+ VALUE channel) {
200
+ VALUE self = RubyCaller.call(IodinePubSubSubscription, iodine_new_func_id);
201
+ if (type == IODINE_PUBSUB_SSE)
202
+ owner = (void *)((uintptr_t)owner | (uintptr_t)1);
203
+ iodine_set_cdata(self, owner);
204
+ iodine_set_fd(self, uuid);
205
+ rb_ivar_set(self, to_str_shadow_id, channel);
206
+ rb_ivar_set(self, iodine_timeout_var_id, ULL2NUM(sub));
112
207
  return self;
113
208
  }
114
209
 
210
+ // static void set_subscription(VALUE self, pubsub_sub_pt sub) {
211
+ // iodine_set_cdata(self, sub);
212
+ // }
213
+
214
+ /** Closes (cancels) a subscription. */
215
+ static VALUE close_subscription(VALUE self) {
216
+ iodine_subscription_s data = subscription_data(self);
217
+ if (!data.subscription)
218
+ return Qnil;
219
+ switch (data.type) {
220
+ case IODINE_PUBSUB_GLOBAL:
221
+ pubsub_unsubscribe((pubsub_sub_pt)data.subscription);
222
+ break;
223
+ case IODINE_PUBSUB_WEBSOCKET:
224
+ websocket_unsubscribe(data.owner, data.subscription);
225
+ break;
226
+ case IODINE_PUBSUB_SSE:
227
+ http_sse_unsubscribe(data.owner, data.subscription);
228
+ break;
229
+ }
230
+ rb_ivar_set(self, iodine_timeout_var_id, ULL2NUM(0));
231
+ return Qnil;
232
+ }
233
+
234
+ /** Test if the subscription's target is equal to String. */
235
+ static VALUE subscription_eq_s(VALUE self, VALUE str) {
236
+ return rb_str_equal(rb_attr_get(self, to_str_shadow_id), str);
237
+ }
238
+
239
+ /** Returns the target stream / channel / pattern as a String object. */
240
+ static VALUE subscription_to_s(VALUE self) {
241
+ return rb_attr_get(self, to_str_shadow_id);
242
+ }
243
+
244
+ /* *****************************************************************************
245
+ Ruby API
246
+ ***************************************************************************** */
247
+
115
248
  pubsub_engine_s *iodine_engine_ruby2facil(VALUE ruby_engine) {
116
249
  if (ruby_engine == Qnil || ruby_engine == Qfalse)
117
250
  return NULL;
@@ -128,73 +261,77 @@ C => Ruby Bridge
128
261
 
129
262
  struct engine_gvl_args_s {
130
263
  const pubsub_engine_s *eng;
131
- const char *ch;
132
- size_t ch_len;
133
- const char *msg;
134
- size_t msg_len;
264
+ FIOBJ ch;
265
+ FIOBJ msg;
135
266
  uint8_t use_pattern;
136
267
  };
137
268
 
138
269
  static void *engine_subscribe_inGVL(void *a_) {
139
270
  struct engine_gvl_args_s *args = a_;
271
+ VALUE eng = ((iodine_engine_s *)args->eng)->handler;
272
+ if (!eng || eng == Qnil || eng == Qfalse)
273
+ return NULL;
140
274
  VALUE data[2];
141
- data[0] = rb_str_new(args->ch, args->ch_len);
275
+ fio_cstr_s tmp = fiobj_obj2cstr(args->ch);
142
276
  data[1] = args->use_pattern ? Qtrue : Qnil;
143
- VALUE eng = ((iodine_engine_s *)args->eng)->handler;
277
+ data[0] = rb_str_new(tmp.data, tmp.len);
144
278
  eng = RubyCaller.call2(eng, engine_subid, 2, data);
145
- return ((eng == Qfalse || eng == Qnil) ? (void *)-1 : (void *)0);
279
+ return NULL;
146
280
  }
147
281
 
148
282
  /* Should return 0 on success and -1 on failure. */
149
- static int engine_subscribe(const pubsub_engine_s *eng, const char *ch,
150
- size_t ch_len, uint8_t use_pattern) {
283
+ static void engine_subscribe(const pubsub_engine_s *eng, FIOBJ ch,
284
+ uint8_t use_pattern) {
151
285
  struct engine_gvl_args_s args = {
152
- .eng = eng, .ch = ch, .ch_len = ch_len, .use_pattern = use_pattern,
286
+ .eng = eng, .ch = ch, .use_pattern = use_pattern,
153
287
  };
154
- return RubyCaller.call_c(engine_subscribe_inGVL, &args) ? 0 : -1;
288
+ RubyCaller.call_c(engine_subscribe_inGVL, &args);
155
289
  }
156
290
 
157
291
  static void *engine_unsubscribe_inGVL(void *a_) {
158
292
  struct engine_gvl_args_s *args = a_;
293
+ VALUE eng = ((iodine_engine_s *)args->eng)->handler;
294
+ if (!eng || eng == Qnil || eng == Qfalse)
295
+ return NULL;
159
296
  VALUE data[2];
160
- data[0] = rb_str_new(args->ch, args->ch_len);
297
+ fio_cstr_s tmp = fiobj_obj2cstr(args->ch);
161
298
  data[1] = args->use_pattern ? Qtrue : Qnil;
162
- VALUE eng = ((iodine_engine_s *)args->eng)->handler;
299
+ data[0] = rb_str_new(tmp.data, tmp.len);
163
300
  RubyCaller.call2(eng, engine_unsubid, 2, data);
164
301
  return NULL;
165
302
  }
166
303
 
167
304
  /* Return value is ignored - nothing should be returned. */
168
- static void engine_unsubscribe(const pubsub_engine_s *eng, const char *ch,
169
- size_t ch_len, uint8_t use_pattern) {
305
+ static void engine_unsubscribe(const pubsub_engine_s *eng, FIOBJ ch,
306
+ uint8_t use_pattern) {
170
307
  struct engine_gvl_args_s args = {
171
- .eng = eng, .ch = ch, .ch_len = ch_len, .use_pattern = use_pattern,
308
+ .eng = eng, .ch = ch, .use_pattern = use_pattern,
172
309
  };
173
310
  RubyCaller.call_c(engine_unsubscribe_inGVL, &args);
174
311
  }
175
312
 
176
313
  static void *engine_publish_inGVL(void *a_) {
177
314
  struct engine_gvl_args_s *args = a_;
178
- VALUE data[3];
179
- data[0] = rb_str_new(args->ch, args->ch_len);
180
- data[1] = rb_str_new(args->msg, args->msg_len);
181
- data[2] = args->use_pattern ? Qtrue : Qnil;
182
315
  VALUE eng = ((iodine_engine_s *)args->eng)->handler;
183
- eng = RubyCaller.call2(eng, engine_pubid, 3, data);
316
+ if (!eng || eng == Qnil || eng == Qfalse)
317
+ return NULL;
318
+ VALUE data[2];
319
+ fio_cstr_s tmp = fiobj_obj2cstr(args->ch);
320
+ data[0] = rb_str_new(tmp.data, tmp.len);
321
+ Registry.add(data[0]);
322
+ tmp = fiobj_obj2cstr(args->msg);
323
+ data[1] = rb_str_new(tmp.data, tmp.len);
324
+ Registry.add(data[1]);
325
+ eng = RubyCaller.call2(eng, iodine_engine_pubid, 2, data);
326
+ Registry.remove(data[0]);
327
+ Registry.remove(data[1]);
184
328
  return ((eng == Qfalse || eng == Qnil) ? (void *)-1 : 0);
185
329
  }
186
330
 
187
331
  /* Should return 0 on success and -1 on failure. */
188
- static int engine_publish(const pubsub_engine_s *eng, const char *ch,
189
- size_t ch_len, const char *msg, size_t msg_len,
190
- uint8_t use_pattern) {
332
+ static int engine_publish(const pubsub_engine_s *eng, FIOBJ ch, FIOBJ msg) {
191
333
  struct engine_gvl_args_s args = {
192
- .eng = eng,
193
- .ch = ch,
194
- .ch_len = ch_len,
195
- .msg = msg,
196
- .msg_len = msg_len,
197
- .use_pattern = use_pattern,
334
+ .eng = eng, .ch = ch, .msg = msg,
198
335
  };
199
336
  return RubyCaller.call_c(engine_publish_inGVL, &args) ? 0 : -1;
200
337
  }
@@ -216,12 +353,12 @@ static void engine_free(void *eng_) {
216
353
  free(eng);
217
354
  }
218
355
 
219
- /* GMP::Integer.allocate */
356
+ /* Iodine::PubSub::Engine.allocate */
220
357
  static VALUE engine_alloc_c(VALUE self) {
221
358
  iodine_engine_s *eng = malloc(sizeof(*eng));
222
359
  if (TYPE(self) == T_CLASS)
223
360
  *eng = (iodine_engine_s){
224
- .handler = self,
361
+ .handler = (VALUE)0,
225
362
  .engine =
226
363
  {
227
364
  .subscribe = engine_subscribe,
@@ -237,6 +374,9 @@ static VALUE engine_alloc_c(VALUE self) {
237
374
  static VALUE engine_initialize(VALUE self) {
238
375
  iodine_engine_s *engine;
239
376
  Data_Get_Struct(self, iodine_engine_s, engine);
377
+ if (TYPE(self) == T_CLASS) {
378
+ fprintf(stderr, "This sucks...\n");
379
+ }
240
380
  engine->handler = self;
241
381
  return self;
242
382
  }
@@ -246,56 +386,29 @@ Redis
246
386
  ***************************************************************************** */
247
387
 
248
388
  struct redis_callback_data {
249
- resp_object_s *msg;
389
+ FIOBJ msg;
250
390
  VALUE block;
251
391
  };
252
392
 
253
- /*
254
- populate
255
- */
256
- int populate_redis_callback_reply(resp_parser_pt p, resp_object_s *o,
257
- void *rep) {
258
- switch (o->type) {
259
- case RESP_ARRAY:
260
- case RESP_PUBSUB:
261
- break;
262
- case RESP_NULL:
263
- rb_ary_push((VALUE)rep, Qnil);
264
- break;
265
- case RESP_NUMBER:
266
- rb_ary_push((VALUE)rep, LONG2FIX(resp_obj2num(o)->number));
267
- break;
268
- case RESP_ERR:
269
- case RESP_STRING:
270
- rb_ary_push((VALUE)rep, rb_str_new((char *)resp_obj2str(o)->string,
271
- resp_obj2str(o)->len));
272
- break;
273
- case RESP_OK:
274
- rb_ary_push((VALUE)rep, rb_str_new("OK", 2));
275
- break;
276
- }
277
- return 0;
278
- (void)p;
279
- }
280
393
  /*
281
394
  Perform a Redis message callback in the GVL
282
395
  */
283
396
  static void *perform_redis_callback_inGVL(void *data) {
284
397
  struct redis_callback_data *a = data;
285
- VALUE reply = rb_ary_new();
286
- resp_obj_each(NULL, a->msg, populate_redis_callback_reply, (void *)reply);
398
+ VALUE reply = fiobj2rb_deep(a->msg, 1);
399
+ Registry.add(reply);
287
400
  rb_funcallv(a->block, iodine_call_proc_id, 1, &reply);
288
401
  Registry.remove(a->block);
402
+ Registry.remove(reply);
289
403
  return NULL;
290
404
  }
291
405
 
292
406
  /*
293
407
  Redis message callback
294
408
  */
295
- static void redis_callback(pubsub_engine_s *e, resp_object_s *msg,
296
- void *block) {
409
+ static void redis_callback(pubsub_engine_s *e, FIOBJ reply, void *block) {
297
410
  struct redis_callback_data d = {
298
- .msg = msg, .block = (VALUE)block,
411
+ .msg = reply, .block = (VALUE)block,
299
412
  };
300
413
  RubyCaller.call_c(perform_redis_callback_inGVL, &d);
301
414
  (void)e;
@@ -318,46 +431,40 @@ static VALUE redis_send(int argc, VALUE *argv, VALUE self) {
318
431
  rb_raise(rb_eArgError,
319
432
  "wrong number of arguments (given %d, expected at least 1).",
320
433
  argc);
321
- resp_object_s *cmd = NULL;
322
434
  Check_Type(argv[0], T_STRING);
323
-
324
- iodine_engine_s *e;
325
- Data_Get_Struct(self, iodine_engine_s, e);
326
- cmd = resp_arr2obj(argc, NULL);
327
- for (int i = 0; i < argc; i++) {
328
- switch (TYPE(argv[i])) {
329
- case T_SYMBOL:
330
- argv[i] = rb_sym2str(argv[i]);
331
- /* Fallthrough */
332
- case T_STRING:
333
- resp_obj2arr(cmd)->array[i] =
334
- resp_str2obj(RSTRING_PTR(argv[i]), RSTRING_LEN(argv[i]));
335
- break;
336
- case T_FIXNUM:
337
- resp_obj2arr(cmd)->array[i] = resp_num2obj(FIX2LONG(argv[i]));
338
- break;
339
- default:
340
- goto error;
341
- break;
435
+ FIOBJ data = FIOBJ_INVALID;
436
+ FIOBJ cmd = FIOBJ_INVALID;
437
+ if (argc > 1) {
438
+ for (int i = 0; i < argc; ++i) {
439
+ if (TYPE(argv[i]) == T_SYMBOL)
440
+ argv[i] = rb_sym2str(argv[i]);
441
+ if (TYPE(argv[i]) != T_FIXNUM)
442
+ Check_Type(argv[i], T_STRING);
443
+ }
444
+ data = fiobj_ary_new();
445
+ for (int i = 0; i < argc; ++i) {
446
+ if (TYPE(argv[i]) == T_FIXNUM)
447
+ fiobj_ary_push(data, fiobj_num_new(FIX2LONG(argv[i])));
448
+ else
449
+ fiobj_ary_push(
450
+ data, fiobj_str_new(RSTRING_PTR(argv[i]), RSTRING_LEN(argv[i])));
342
451
  }
343
452
  }
453
+ cmd = fiobj_str_new(RSTRING_PTR(argv[0]), RSTRING_LEN(argv[0]));
454
+ iodine_engine_s *e;
455
+ Data_Get_Struct(self, iodine_engine_s, e);
344
456
 
345
457
  if (rb_block_given_p()) {
346
458
  VALUE block = rb_block_proc();
347
459
  Registry.add(block);
348
- redis_engine_send(e->p, cmd, redis_callback, (void *)block);
460
+ redis_engine_send(e->p, cmd, data, redis_callback, (void *)block);
349
461
  return block;
350
462
  } else {
351
- redis_engine_send(e->p, cmd, NULL, NULL);
463
+ redis_engine_send(e->p, cmd, data, NULL, NULL);
352
464
  }
465
+ fiobj_free(cmd);
466
+ fiobj_free(data);
353
467
  return Qtrue;
354
- error:
355
- if (cmd)
356
- resp_free_object(cmd);
357
- rb_raise(rb_eArgError, "Arguments can only include Strings, Symbols and "
358
- "Integers - no arrays or hashes or other objects can "
359
- "be sent.");
360
- return self;
361
468
  }
362
469
 
363
470
  /**
@@ -371,7 +478,7 @@ Accepts:
371
478
 
372
479
  address:: the Redis server's address. Required.
373
480
  port:: the Redis Server port. Default: 6379
374
- ping:: the PING interval. Default: 0 (~5 minutes).
481
+ ping:: the PING interval up to 255 seconds. Default: 0 (~5 minutes).
375
482
  auth:: authentication password. Default: none.
376
483
  */
377
484
  static VALUE redis_engine_initialize(int argc, VALUE *argv, VALUE self) {
@@ -407,9 +514,9 @@ static VALUE redis_engine_initialize(int argc, VALUE *argv, VALUE self) {
407
514
  .ping_interval = iping,
408
515
  .auth = (auth == Qnil ? NULL : StringValueCStr(auth)),
409
516
  .auth_len = (auth == Qnil ? 0 : RSTRING_LEN(auth)));
517
+ engine->dealloc = redis_engine_destroy;
410
518
  if (!engine->p)
411
519
  rb_raise(rb_eRuntimeError, "unknown error, can't initialize RedisEngine.");
412
- engine->dealloc = redis_engine_destroy;
413
520
  return self;
414
521
  }
415
522
 
@@ -429,9 +536,17 @@ static VALUE ips_set_default(VALUE self, VALUE en) {
429
536
  rb_raise(rb_eArgError, "deafult engine must be an Iodine::PubSub::Engine.");
430
537
  if (!e->p)
431
538
  rb_raise(rb_eArgError, "This Iodine::PubSub::Engine is broken.");
432
- rb_ivar_set(self, default_pubsubid, en);
539
+ rb_ivar_set(Iodine, default_pubsubid, en);
433
540
  PUBSUB_DEFAULT_ENGINE = e->p;
434
541
  return en;
542
+ (void)self;
543
+ }
544
+
545
+ /** Deprecated. Use {Iodine::PubSub.default_engine=}. */
546
+ static VALUE ips_set_default_dep(VALUE self, VALUE en) {
547
+ fprintf(stderr, "WARNING: Iodine.default_pubsub is deprecated. Use "
548
+ "Iodine::PubSub.default_engine.\n");
549
+ return ips_set_default(self, en);
435
550
  }
436
551
 
437
552
  /**
@@ -440,7 +555,17 @@ Returns the default Pub/Sub engine (if any).
440
555
  See {Iodine::PubSub} and {Iodine::PubSub::Engine} for more details.
441
556
  */
442
557
  static VALUE ips_get_default(VALUE self) {
443
- return rb_ivar_get(self, default_pubsubid);
558
+ return rb_ivar_get(Iodine, default_pubsubid);
559
+ (void)self;
560
+ }
561
+
562
+ /**
563
+ Deprecated. Use {Iodine::PubSub.default_engine}.
564
+ */
565
+ static VALUE ips_get_default_dep(VALUE self) {
566
+ fprintf(stderr, "WARNING: Iodine.default_pubsub is deprecated. Use "
567
+ "Iodine::PubSub.default_engine.\n");
568
+ return ips_get_default(self);
444
569
  }
445
570
 
446
571
  /* *****************************************************************************
@@ -455,9 +580,11 @@ static void iodine_on_unsubscribe(void *u1, void *u2) {
455
580
 
456
581
  static void *on_pubsub_notificationinGVL(pubsub_message_s *n) {
457
582
  VALUE rbn[2];
458
- rbn[0] = rb_str_new(n->channel.name, n->channel.len);
583
+ fio_cstr_s tmp = fiobj_obj2cstr(n->channel);
584
+ rbn[0] = rb_str_new(tmp.data, tmp.len);
459
585
  Registry.add(rbn[0]);
460
- rbn[1] = rb_str_new(n->msg.data, n->msg.len);
586
+ tmp = fiobj_obj2cstr(n->message);
587
+ rbn[1] = rb_str_new(tmp.data, tmp.len);
461
588
  Registry.add(rbn[1]);
462
589
  RubyCaller.call2((VALUE)n->udata1, iodine_call_proc_id, 2, rbn);
463
590
  Registry.remove(rbn[0]);
@@ -469,116 +596,256 @@ static void on_pubsub_notificationin(pubsub_message_s *n) {
469
596
  RubyCaller.call_c((void *(*)(void *))on_pubsub_notificationinGVL, n);
470
597
  }
471
598
 
472
- /**
473
- Subscribes the process to a channel belonging to a specific pub/sub service
474
- (using an {Iodine::PubSub::Engine} to connect Iodine to the service).
475
-
476
- The function accepts a single argument (a Hash) and a required block.
599
+ static void iodine_on_unsubscribe_ws(void *u) {
600
+ if (u && (VALUE)u != Qnil && u != (VALUE)Qfalse)
601
+ Registry.remove((VALUE)u);
602
+ }
477
603
 
478
- Accepts a single Hash argument with the following possible options:
604
+ static void *
605
+ on_pubsub_notificationinGVL_ws(websocket_pubsub_notification_s *n) {
606
+ VALUE rbn[2];
607
+ fio_cstr_s tmp = fiobj_obj2cstr(n->channel);
608
+ rbn[0] = rb_str_new(tmp.data, tmp.len);
609
+ Registry.add(rbn[0]);
610
+ tmp = fiobj_obj2cstr(n->message);
611
+ rbn[1] = rb_str_new(tmp.data, tmp.len);
612
+ Registry.add(rbn[1]);
613
+ RubyCaller.call2((VALUE)n->udata, iodine_call_proc_id, 2, rbn);
614
+ Registry.remove(rbn[0]);
615
+ Registry.remove(rbn[1]);
616
+ return NULL;
617
+ }
479
618
 
480
- :engine :: If provided, the engine to use for pub/sub. Otherwise the default
481
- :: engine is used.
619
+ static void on_pubsub_notificationin_ws(websocket_pubsub_notification_s n) {
620
+ RubyCaller.call_c((void *(*)(void *))on_pubsub_notificationinGVL_ws, &n);
621
+ }
482
622
 
483
- :channel :: Required (unless :pattern). The channel to subscribe to.
623
+ static void on_pubsub_notificationin_sse(http_sse_s *sse, FIOBJ channel,
624
+ FIOBJ message, void *udata) {
625
+ websocket_pubsub_notification_s n = {
626
+ .channel = channel, .message = message, .udata = udata};
627
+ RubyCaller.call_c((void *(*)(void *))on_pubsub_notificationinGVL, &n);
628
+ (void)sse;
629
+ }
484
630
 
485
- :pattern :: An alternative to the required :channel, subscribes to a pattern.
631
+ /** Subscribes to a Pub/Sub channel - internal implementation */
632
+ VALUE iodine_subscribe(int argc, VALUE *argv, void *owner,
633
+ iodine_pubsub_type_e type) {
486
634
 
487
- */
488
- static VALUE iodine_subscribe(VALUE self, VALUE args) {
489
- Check_Type(args, T_HASH);
490
- rb_need_block();
635
+ VALUE rb_ch = Qnil;
636
+ VALUE rb_opt = 0;
637
+ VALUE block = 0;
638
+ uint8_t use_pattern = 0, force_text = 1, force_binary = 0;
639
+ intptr_t uuid = 0;
491
640
 
492
- uint8_t use_pattern = 0;
641
+ switch (argc) {
642
+ case 2:
643
+ rb_ch = argv[0];
644
+ rb_opt = argv[1];
645
+ break;
646
+ case 1:
647
+ /* single argument must be a Hash / channel name */
648
+ if (TYPE(argv[0]) == T_HASH) {
649
+ rb_opt = argv[0];
650
+ rb_ch = rb_hash_aref(argv[0], to_sym_id);
651
+ if (rb_ch == Qnil || rb_ch == Qfalse) {
652
+ /* temporary backport support */
653
+ rb_ch = rb_hash_aref(argv[0], channel_sym_id);
654
+ if (rb_ch) {
655
+ fprintf(stderr,
656
+ "WARNING: use of :channel in subscribe is deprecated.\n");
657
+ }
658
+ }
659
+ } else {
660
+ rb_ch = argv[0];
661
+ }
662
+ break;
663
+ default:
664
+ rb_raise(rb_eArgError, "method accepts 1 or 2 arguments.");
665
+ return Qnil;
666
+ }
493
667
 
494
- VALUE rb_ch = rb_hash_aref(args, channel_var_id);
495
668
  if (rb_ch == Qnil || rb_ch == Qfalse) {
496
- use_pattern = 1;
497
- rb_ch = rb_hash_aref(args, pattern_var_id);
498
- if (rb_ch == Qnil || rb_ch == Qfalse)
499
- rb_raise(rb_eArgError, "a channel is required for pub/sub methods.");
669
+ rb_raise(rb_eArgError,
670
+ "a target (:to) subject / stream / channel is required.");
500
671
  }
672
+
501
673
  if (TYPE(rb_ch) == T_SYMBOL)
502
674
  rb_ch = rb_sym2str(rb_ch);
503
675
  Check_Type(rb_ch, T_STRING);
504
676
 
505
- VALUE block = rb_block_proc();
506
- Registry.add(block);
677
+ if (rb_opt) {
678
+ if (type == IODINE_PUBSUB_WEBSOCKET &&
679
+ rb_hash_aref(rb_opt, as_sym_id) == binary_sym_id) {
680
+ force_text = 0;
681
+ force_binary = 1;
682
+ }
683
+ if (rb_hash_aref(rb_opt, match_sym_id) == redis_sym_id) {
684
+ use_pattern = 1;
685
+ }
686
+ block = rb_hash_aref(rb_opt, handler_sym_id);
687
+ if (block != Qnil)
688
+ Registry.add(block);
689
+ }
690
+
691
+ if (block == Qnil) {
692
+ if (rb_block_given_p()) {
693
+ block = rb_block_proc();
694
+ Registry.add(block);
695
+ } else if (type == IODINE_PUBSUB_GLOBAL) {
696
+ rb_need_block();
697
+ return Qnil;
698
+ }
699
+ }
700
+ if (block == Qnil)
701
+ block = 0;
702
+
703
+ FIOBJ ch = fiobj_str_new(RSTRING_PTR(rb_ch), RSTRING_LEN(rb_ch));
704
+
705
+ uintptr_t sub = 0;
706
+ switch (type) {
707
+ case IODINE_PUBSUB_GLOBAL:
708
+ sub = (uintptr_t)pubsub_subscribe(.channel = ch, .use_pattern = use_pattern,
709
+ .on_message = on_pubsub_notificationin,
710
+ .on_unsubscribe = iodine_on_unsubscribe,
711
+ .udata1 = (void *)block);
712
+ break;
713
+ case IODINE_PUBSUB_WEBSOCKET:
714
+ uuid = websocket_uuid(owner);
715
+ sub = websocket_subscribe(
716
+ owner, .channel = ch, .use_pattern = use_pattern,
717
+ .force_text = force_text, .force_binary = force_binary,
718
+ .on_message = (block ? on_pubsub_notificationin_ws : NULL),
719
+ .on_unsubscribe = (block ? iodine_on_unsubscribe_ws : NULL),
720
+ .udata = (void *)block);
721
+ break;
722
+ case IODINE_PUBSUB_SSE:
723
+ uuid = http_sse2uuid(owner);
724
+ sub = http_sse_subscribe(
725
+ owner, .channel = ch, .use_pattern = use_pattern,
726
+ .on_message = (block ? on_pubsub_notificationin_sse : NULL),
727
+ .on_unsubscribe = (block ? iodine_on_unsubscribe_ws : NULL),
728
+ .udata = (void *)block);
507
729
 
508
- pubsub_engine_s *engine =
509
- iodine_engine_ruby2facil(rb_hash_aref(args, engine_varid));
730
+ break;
731
+ }
510
732
 
511
- uintptr_t subid =
512
- (uintptr_t)pubsub_subscribe(.channel.name = RSTRING_PTR(rb_ch),
513
- .channel.len = RSTRING_LEN(rb_ch),
514
- .engine = engine, .use_pattern = use_pattern,
515
- .on_message = on_pubsub_notificationin,
516
- .on_unsubscribe = iodine_on_unsubscribe,
517
- .udata1 = (void *)block);
518
- if (!subid)
733
+ fiobj_free(ch);
734
+ if (!sub)
519
735
  return Qnil;
520
- return ULL2NUM(subid);
521
- (void)self;
736
+ return subscription_initialize(sub, uuid, owner, type, rb_ch);
522
737
  }
523
738
 
739
+ // clang-format off
524
740
  /**
525
- Cancels the subscription matching `sub_id`.
741
+ Subscribes to a Pub/Sub channel.
742
+
743
+ The method accepts 1-2 arguments and an optional block. These are all valid ways
744
+ to call the method:
745
+
746
+ subscribe("my_stream") {|from, msg| p msg }
747
+ subscribe("my_stream", match: :redis) {|from, msg| p msg }
748
+ subscribe(to: "my_stream") {|from, msg| p msg }
749
+ subscribe to: "my_stream", match: :redis, handler: MyProc
750
+
751
+ The first argument must be either a String or a Hash.
752
+
753
+ The second, optional, argument must be a Hash (if given).
754
+
755
+ The options Hash supports the following possible keys (other keys are ignored, all keys are Symbols):
756
+
757
+ :match :: The channel / subject name matching type to be used. Valid value is: `:redis`. Future versions hope to support `:nats` and `:rabbit` patern matching as well.
758
+
759
+ :to :: The channel / subject to subscribe to.
760
+
761
+ Returns an {Iodine::PubSub::Subscription} object that answers to:
762
+
763
+ close :: closes the connection.
764
+ to_s :: returns the subscription's target (stream / channel / subject).
765
+ ==(str) :: returns true if the string is an exact match for the target (even if the target itself is a pattern).
766
+
526
767
  */
527
- static VALUE iodine_unsubscribe(VALUE self, VALUE sub_id) {
528
- if (sub_id == Qnil || sub_id == Qfalse)
529
- return Qnil;
530
- Check_Type(sub_id, T_FIXNUM);
531
- pubsub_unsubscribe((pubsub_sub_pt)NUM2LONG(sub_id));
532
- return Qnil;
768
+ static VALUE iodine_subscribe_global(int argc, VALUE *argv, VALUE self) {
769
+ // clang-format on
770
+ return iodine_subscribe(argc, argv, NULL, IODINE_PUBSUB_GLOBAL);
533
771
  (void)self;
534
772
  }
535
773
 
536
774
  /**
537
775
  Publishes a message to a channel.
538
776
 
539
- Accepts a single Hash argument with the following possible options:
777
+ Can be used using two Strings:
778
+
779
+ publish(to, message)
780
+
781
+ The method accepts an optional `engine` argument:
782
+
783
+ publish(to, message, my_pubsub_engine)
540
784
 
541
- :engine :: If provided, the engine to use for pub/sub. Otherwise the default
542
- engine is used.
543
785
 
544
- :channel :: Required (unless :pattern). The channel to publish to.
786
+ Alternatively, accepts the following named arguments:
545
787
 
546
- :pattern :: An alternative to the required :channel, publishes to a pattern.
547
- This is NOT supported by Redis and it's limited to the local process cluster.
788
+ :to :: The channel to publish to (required).
789
+
790
+ :message :: The message to be published (required).
791
+
792
+ :engine :: If provided, the engine to use for pub/sub. Otherwise the default
793
+ engine is used.
548
794
 
549
- :message :: REQUIRED. The message to be published.
550
- :
551
795
  */
552
- static VALUE iodine_publish(VALUE self, VALUE args) {
553
- Check_Type(args, T_HASH);
796
+ VALUE iodine_publish(int argc, VALUE *argv, VALUE self) {
797
+ VALUE rb_ch, rb_msg, rb_engine = Qnil;
554
798
  uint8_t use_pattern = 0;
799
+ const pubsub_engine_s *engine = NULL;
800
+ switch (argc) {
801
+ case 3:
802
+ /* fallthrough */
803
+ rb_engine = argv[2];
804
+ case 2:
805
+ rb_ch = argv[0];
806
+ rb_msg = argv[1];
807
+ break;
808
+ case 1: {
809
+ /* single argument must be a Hash */
810
+ Check_Type(argv[0], T_HASH);
811
+ rb_ch = rb_hash_aref(argv[0], to_sym_id);
812
+ if (rb_ch == Qnil || rb_ch == Qfalse) {
813
+ use_pattern = 1;
814
+ rb_ch = rb_hash_aref(argv[0], match_sym_id);
815
+ }
816
+ rb_msg = rb_hash_aref(argv[0], message_sym_id);
817
+ rb_engine = rb_hash_aref(argv[0], engine_varid);
818
+ } break;
819
+ default:
820
+ rb_raise(rb_eArgError, "method accepts 1-3 arguments.");
821
+ }
555
822
 
556
- VALUE rb_ch = rb_hash_aref(args, channel_var_id);
557
- if (rb_ch == Qnil || rb_ch == Qfalse) {
558
- use_pattern = 1;
559
- rb_ch = rb_hash_aref(args, pattern_var_id);
560
- if (rb_ch == Qnil || rb_ch == Qfalse)
561
- rb_raise(rb_eArgError, "channel is required for pub/sub methods.");
823
+ if (rb_msg == Qnil || rb_msg == Qfalse) {
824
+ rb_raise(rb_eArgError, "message is required.");
562
825
  }
826
+ Check_Type(rb_msg, T_STRING);
827
+
828
+ if (rb_ch == Qnil || rb_ch == Qfalse)
829
+ rb_raise(rb_eArgError, "channel is required .");
563
830
  if (TYPE(rb_ch) == T_SYMBOL)
564
831
  rb_ch = rb_sym2str(rb_ch);
565
832
  Check_Type(rb_ch, T_STRING);
566
833
 
567
- VALUE rb_msg = rb_hash_aref(args, message_var_id);
568
- if (rb_msg == Qnil || rb_msg == Qfalse) {
569
- rb_raise(rb_eArgError, "message is required for the :publish method.");
834
+ if (rb_engine == Qfalse) {
835
+ engine = PUBSUB_PROCESS_ENGINE;
836
+ } else if (rb_engine == Qnil) {
837
+ engine = NULL;
838
+ } else {
839
+ engine = iodine_engine_ruby2facil(rb_engine);
570
840
  }
571
- Check_Type(rb_msg, T_STRING);
572
841
 
573
- pubsub_engine_s *engine =
574
- iodine_engine_ruby2facil(rb_hash_aref(args, engine_varid));
842
+ FIOBJ ch = fiobj_str_new(RSTRING_PTR(rb_ch), RSTRING_LEN(rb_ch));
843
+ FIOBJ msg = fiobj_str_new(RSTRING_PTR(rb_msg), RSTRING_LEN(rb_msg));
575
844
 
576
845
  intptr_t ret =
577
- pubsub_publish(.engine = engine, .channel.name = (RSTRING_PTR(rb_ch)),
578
- .channel.len = (RSTRING_LEN(rb_ch)),
579
- .msg.data = (RSTRING_PTR(rb_msg)),
580
- .msg.len = (RSTRING_LEN(rb_msg)),
581
- .use_pattern = use_pattern);
846
+ pubsub_publish(.engine = engine, .channel = ch, .message = msg);
847
+ fiobj_free(ch);
848
+ fiobj_free(msg);
582
849
  if (!ret)
583
850
  return Qfalse;
584
851
  return Qtrue;
@@ -589,32 +856,64 @@ static VALUE iodine_publish(VALUE self, VALUE args) {
589
856
  Initialization
590
857
  ***************************************************************************** */
591
858
  void Iodine_init_pubsub(void) {
592
- engine_varid = rb_intern("engine");
859
+ default_pubsubid = rb_intern("default_pubsub");
593
860
  engine_subid = rb_intern("subscribe");
594
861
  engine_unsubid = rb_intern("unsubscribe");
595
- engine_pubid = rb_intern("publish");
596
- default_pubsubid = rb_intern("default_pubsub");
597
- channel_var_id = ID2SYM(rb_intern("channel"));
598
- pattern_var_id = ID2SYM(rb_intern("pattern"));
599
- message_var_id = ID2SYM(rb_intern("message"));
862
+ engine_varid = rb_intern("engine");
863
+ iodine_engine_pubid = rb_intern("publish");
864
+ to_str_shadow_id = rb_intern("@to_s");
865
+
866
+ as_sym_id = ID2SYM(rb_intern("as"));
867
+ binary_sym_id = ID2SYM(rb_intern("binary"));
868
+ handler_sym_id = ID2SYM(rb_intern("handler"));
869
+ match_sym_id = ID2SYM(rb_intern("match"));
870
+ message_sym_id = ID2SYM(rb_intern("message"));
871
+ redis_sym_id = ID2SYM(rb_intern("redis"));
872
+ text_sym_id = ID2SYM(rb_intern("text"));
873
+ to_sym_id = ID2SYM(rb_intern("to"));
874
+
875
+ channel_sym_id = ID2SYM(rb_intern("channel")); /* bawards compatibility */
600
876
 
601
877
  IodinePubSub = rb_define_module_under(Iodine, "PubSub");
602
878
  IodineEngine = rb_define_class_under(IodinePubSub, "Engine", rb_cObject);
879
+ IodinePubSubSubscription =
880
+ rb_define_class_under(IodinePubSub, "Subscription", rb_cObject);
881
+
882
+ rb_define_method(IodinePubSubSubscription, "close", close_subscription, 0);
883
+ rb_define_method(IodinePubSubSubscription, "==", subscription_eq_s, 1);
884
+ rb_attr(IodinePubSubSubscription, rb_intern("to_s"), 1, 0, 1);
885
+ rb_define_method(IodinePubSubSubscription, "to_s", subscription_to_s, 1);
603
886
 
604
887
  rb_define_alloc_func(IodineEngine, engine_alloc_c);
605
888
  rb_define_method(IodineEngine, "initialize", engine_initialize, 0);
606
889
 
607
- rb_define_method(IodineEngine, "distribute", engine_distribute, -1);
608
890
  rb_define_method(IodineEngine, "subscribe", engine_sub_placeholder, 2);
609
891
  rb_define_method(IodineEngine, "unsubscribe", engine_sub_placeholder, 2);
610
- rb_define_method(IodineEngine, "publish", engine_pub_placeholder, 3);
892
+ rb_define_method(IodineEngine, "publish", engine_pub_placeholder, 2);
893
+ rb_define_method(IodineEngine, "register", iodine_engine_register, 0);
894
+ rb_define_method(IodineEngine, "deregister", iodine_engine_deregister, 0);
895
+ rb_define_method(IodineEngine, "reset", iodine_engine_reset, 0);
611
896
 
612
- rb_define_module_function(Iodine, "default_pubsub=", ips_set_default, 1);
613
- rb_define_module_function(Iodine, "default_pubsub", ips_get_default, 0);
897
+ rb_define_module_function(Iodine, "subscribe", iodine_subscribe_global, -1);
898
+ rb_define_module_function(Iodine, "publish", iodine_publish, -1);
899
+ rb_define_module_function(Iodine, "default_engine=", ips_set_default, 1);
900
+ rb_define_module_function(Iodine, "default_engine", ips_get_default, 0);
614
901
 
615
- rb_define_module_function(Iodine, "subscribe", iodine_subscribe, 1);
616
- rb_define_module_function(Iodine, "unsubscribe", iodine_unsubscribe, 1);
617
- rb_define_module_function(Iodine, "publish", iodine_publish, 1);
902
+ rb_define_module_function(IodinePubSub, "default_engine=", ips_set_default,
903
+ 1);
904
+ rb_define_module_function(IodinePubSub, "default_engine", ips_get_default, 0);
905
+ rb_define_method(IodinePubSub, "register", iodine_engine_register2, 1);
906
+ rb_define_method(IodinePubSub, "deregister", iodine_engine_deregister2, 1);
907
+ rb_define_method(IodinePubSub, "reset", iodine_engine_reset2, 1);
908
+
909
+ rb_define_module_function(IodinePubSub, "subscribe", iodine_subscribe_global,
910
+ -1);
911
+ rb_define_module_function(IodinePubSub, "publish", iodine_publish, -1);
912
+
913
+ /* deprecated */
914
+
915
+ rb_define_module_function(Iodine, "default_pubsub=", ips_set_default_dep, 1);
916
+ rb_define_module_function(Iodine, "default_pubsub", ips_get_default_dep, 0);
618
917
 
619
918
  /* *************************
620
919
  Initialize C pubsub engines
@@ -629,6 +928,7 @@ void Iodine_init_pubsub(void) {
629
928
  /** This is the (currently) default pub/sub engine. It will distribute
630
929
  * messages to all subscribers in the process cluster. */
631
930
  rb_define_const(IodinePubSub, "CLUSTER", engine_in_c);
931
+
632
932
  // rb_const_set(IodineEngine, rb_intern("CLUSTER"), e);
633
933
 
634
934
  engine_in_c = rb_funcallv(IodineEngine, iodine_new_func_id, 0, NULL);
@@ -638,6 +938,7 @@ void Iodine_init_pubsub(void) {
638
938
  /** This is a single process pub/sub engine. It will distribute messages to
639
939
  * all subscribers sharing the same process. */
640
940
  rb_define_const(IodinePubSub, "SINGLE_PROCESS", engine_in_c);
941
+
641
942
  // rb_const_set(IodineEngine, rb_intern("SINGLE_PROCESS"), e);
642
943
 
643
944
  engine_in_c =