iodine 0.5.2 → 0.6.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -0
- data/README.md +63 -100
- data/bin/raw-rbhttp +12 -7
- data/examples/config.ru +8 -7
- data/examples/echo.ru +8 -7
- data/examples/info.md +41 -35
- data/examples/pubsub_engine.ru +12 -12
- data/examples/redis.ru +10 -12
- data/examples/shootout.ru +19 -42
- data/exe/iodine +116 -1
- data/ext/iodine/defer.c +1 -1
- data/ext/iodine/facil.c +12 -8
- data/ext/iodine/facil.h +2 -2
- data/ext/iodine/iodine.c +177 -343
- data/ext/iodine/iodine.h +18 -72
- data/ext/iodine/iodine_caller.c +132 -0
- data/ext/iodine/iodine_caller.h +21 -0
- data/ext/iodine/iodine_connection.c +841 -0
- data/ext/iodine/iodine_connection.h +55 -0
- data/ext/iodine/iodine_defer.c +391 -0
- data/ext/iodine/iodine_defer.h +7 -0
- data/ext/iodine/{rb-fiobj2rb.h → iodine_fiobj2rb.h} +6 -6
- data/ext/iodine/iodine_helpers.c +51 -5
- data/ext/iodine/iodine_helpers.h +2 -3
- data/ext/iodine/iodine_http.c +284 -141
- data/ext/iodine/iodine_http.h +2 -2
- data/ext/iodine/iodine_json.c +13 -13
- data/ext/iodine/iodine_json.h +1 -1
- data/ext/iodine/iodine_pubsub.c +573 -823
- data/ext/iodine/iodine_pubsub.h +15 -27
- data/ext/iodine/{rb-rack-io.c → iodine_rack_io.c} +30 -8
- data/ext/iodine/{rb-rack-io.h → iodine_rack_io.h} +1 -0
- data/ext/iodine/iodine_store.c +136 -0
- data/ext/iodine/iodine_store.h +20 -0
- data/ext/iodine/iodine_tcp.c +385 -0
- data/ext/iodine/iodine_tcp.h +9 -0
- data/lib/iodine.rb +73 -171
- data/lib/iodine/connection.rb +34 -0
- data/lib/iodine/pubsub.rb +5 -18
- data/lib/iodine/rack_utils.rb +43 -0
- data/lib/iodine/version.rb +1 -1
- data/lib/rack/handler/iodine.rb +1 -182
- metadata +17 -18
- data/ext/iodine/iodine_protocol.c +0 -689
- data/ext/iodine/iodine_protocol.h +0 -13
- data/ext/iodine/iodine_websockets.c +0 -550
- data/ext/iodine/iodine_websockets.h +0 -17
- data/ext/iodine/rb-call.c +0 -156
- data/ext/iodine/rb-call.h +0 -70
- data/ext/iodine/rb-defer.c +0 -124
- data/ext/iodine/rb-registry.c +0 -150
- data/ext/iodine/rb-registry.h +0 -34
- data/lib/iodine/cli.rb +0 -89
- data/lib/iodine/monkeypatch.rb +0 -46
- data/lib/iodine/protocol.rb +0 -42
- data/lib/iodine/websocket.rb +0 -16
data/ext/iodine/iodine_http.h
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
#ifndef H_IODINE_HTTP_H
|
2
2
|
#define H_IODINE_HTTP_H
|
3
3
|
/*
|
4
|
-
Copyright: Boaz segev, 2016-
|
4
|
+
Copyright: Boaz segev, 2016-2018
|
5
5
|
License: MIT
|
6
6
|
|
7
7
|
Feel free to copy, use and enjoy according to the license provided.
|
@@ -13,6 +13,6 @@ extern VALUE IODINE_R_HIJACK;
|
|
13
13
|
extern VALUE IODINE_R_HIJACK_IO;
|
14
14
|
extern VALUE IODINE_R_HIJACK_CB;
|
15
15
|
|
16
|
-
void
|
16
|
+
void iodine_init_http(void);
|
17
17
|
|
18
18
|
#endif
|
data/ext/iodine/iodine_json.c
CHANGED
@@ -4,8 +4,8 @@
|
|
4
4
|
#include "fio_json_parser.h"
|
5
5
|
#include "fio_mem.h"
|
6
6
|
#include "fiobj.h"
|
7
|
-
#include "
|
8
|
-
#include "
|
7
|
+
#include "iodine_fiobj2rb.h"
|
8
|
+
#include "iodine_store.h"
|
9
9
|
|
10
10
|
static VALUE max_nesting;
|
11
11
|
static VALUE allow_nan;
|
@@ -37,20 +37,20 @@ static inline void iodine_json_add2parser(iodine_json_parser_s *p, VALUE o) {
|
|
37
37
|
if (p->is_hash) {
|
38
38
|
if (p->key) {
|
39
39
|
rb_hash_aset(p->top, p->key, o);
|
40
|
-
|
40
|
+
IodineStore.remove(p->key);
|
41
41
|
p->key = (VALUE)0;
|
42
42
|
} else {
|
43
43
|
// if (p->symbolize) {
|
44
44
|
// o = rb_to_symbol(o);
|
45
45
|
// }
|
46
46
|
p->key = o;
|
47
|
-
|
47
|
+
IodineStore.add(o);
|
48
48
|
}
|
49
49
|
} else {
|
50
50
|
rb_ary_push(p->top, o);
|
51
51
|
}
|
52
52
|
} else {
|
53
|
-
|
53
|
+
IodineStore.add(o);
|
54
54
|
p->top = o;
|
55
55
|
}
|
56
56
|
}
|
@@ -115,7 +115,7 @@ static void fio_json_on_end_object(json_parser_s *p) {
|
|
115
115
|
if (pr->key) {
|
116
116
|
fprintf(stderr, "WARNING: (JSON parsing) malformed JSON, "
|
117
117
|
"ignoring dangling Hash key.\n");
|
118
|
-
|
118
|
+
IodineStore.remove(pr->key);
|
119
119
|
pr->key = (VALUE)0;
|
120
120
|
}
|
121
121
|
pr->top = (VALUE)fio_ary_pop(&pr->stack);
|
@@ -147,8 +147,8 @@ static void fio_json_on_error(json_parser_s *p) {
|
|
147
147
|
#if DEBUG
|
148
148
|
fprintf(stderr, "ERROR: JSON on error called.\n");
|
149
149
|
#endif
|
150
|
-
|
151
|
-
|
150
|
+
IodineStore.remove((VALUE)fio_ary_index(&pr->stack, 0));
|
151
|
+
IodineStore.remove(pr->key);
|
152
152
|
fio_ary_free(&pr->stack);
|
153
153
|
*pr = (iodine_json_parser_s){.top = 0};
|
154
154
|
}
|
@@ -162,17 +162,17 @@ static inline VALUE iodine_json_convert(VALUE str, fiobj2rb_settings_s s) {
|
|
162
162
|
iodine_json_parser_s p = {.top = 0, .symbolize = s.str2sym};
|
163
163
|
size_t consumed = fio_json_parse(&p.p, RSTRING_PTR(str), RSTRING_LEN(str));
|
164
164
|
if (!consumed || p.p.depth) {
|
165
|
-
|
165
|
+
IodineStore.remove((VALUE)fio_ary_index(&p.stack, 0));
|
166
166
|
p.top = FIOBJ_INVALID;
|
167
167
|
}
|
168
168
|
fio_ary_free(&p.stack);
|
169
169
|
if (p.key) {
|
170
|
-
|
170
|
+
IodineStore.remove((VALUE)p.key);
|
171
171
|
}
|
172
172
|
if (!p.top) {
|
173
173
|
rb_raise(rb_eEncodingError, "Malformed JSON format.");
|
174
174
|
}
|
175
|
-
|
175
|
+
IodineStore.remove(p.top);
|
176
176
|
return p.top;
|
177
177
|
}
|
178
178
|
|
@@ -253,7 +253,7 @@ static VALUE iodine_json_parse_bang(int argc, VALUE *argv, VALUE self) {
|
|
253
253
|
(void)self;
|
254
254
|
}
|
255
255
|
|
256
|
-
void
|
256
|
+
void iodine_init_json(void) {
|
257
257
|
/**
|
258
258
|
Iodine::JSON offers a fast(er) JSON parser that is also lenient and supports
|
259
259
|
some JSON extensions such as Hex number recognition and comments.
|
@@ -292,7 +292,7 @@ void Iodine_init_json(void) {
|
|
292
292
|
|
293
293
|
|
294
294
|
*/
|
295
|
-
VALUE tmp = rb_define_module_under(
|
295
|
+
VALUE tmp = rb_define_module_under(IodineModule, "JSON");
|
296
296
|
max_nesting = ID2SYM(rb_intern("max_nesting"));
|
297
297
|
allow_nan = ID2SYM(rb_intern("allow_nan"));
|
298
298
|
symbolize_names = ID2SYM(rb_intern("symbolize_names"));
|
data/ext/iodine/iodine_json.h
CHANGED
data/ext/iodine/iodine_pubsub.c
CHANGED
@@ -1,946 +1,696 @@
|
|
1
|
-
/*
|
2
|
-
Copyright: Boaz segev, 2016-2017
|
3
|
-
License: MIT
|
4
|
-
|
5
|
-
Feel free to copy, use and enjoy according to the license provided.
|
6
|
-
*/
|
7
1
|
#include "iodine_pubsub.h"
|
8
|
-
#include "
|
9
|
-
|
2
|
+
#include "iodine_fiobj2rb.h"
|
10
3
|
#include "pubsub.h"
|
11
|
-
#include "rb-fiobj2rb.h"
|
12
4
|
#include "redis_engine.h"
|
13
|
-
#include "websockets.h"
|
14
|
-
|
15
|
-
VALUE IodineEngine;
|
16
|
-
ID iodine_engine_pubid;
|
17
|
-
|
18
|
-
static VALUE IodinePubSub;
|
19
|
-
static VALUE IodinePubSubSubscription;
|
20
|
-
static ID engine_varid;
|
21
|
-
static ID engine_subid;
|
22
|
-
static ID engine_unsubid;
|
23
|
-
static ID default_pubsubid;
|
24
|
-
|
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;
|
36
5
|
|
37
|
-
/*
|
38
|
-
|
39
|
-
***************************************************************************** */
|
6
|
+
/*
|
7
|
+
NOTE:
|
40
8
|
|
41
|
-
|
42
|
-
Override this method to handle (un)subscription requests.
|
9
|
+
This file defines Pub/Sub management and settings, not Pub/Sub usage.
|
43
10
|
|
44
|
-
This
|
45
|
-
|
11
|
+
This file doen't include the `Iodine.subscribe`, `Iodine.unsubscribe` and
|
12
|
+
`Iodine.publish` methods.
|
46
13
|
|
47
|
-
|
14
|
+
These methods are all defined in the Connection module (iodine_connection.h).
|
48
15
|
*/
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
16
|
+
|
17
|
+
/* *****************************************************************************
|
18
|
+
static consts
|
19
|
+
***************************************************************************** */
|
20
|
+
|
21
|
+
static ID subscribe_id;
|
22
|
+
static ID unsubscribe_id;
|
23
|
+
static ID publish_id;
|
24
|
+
static ID default_id;
|
25
|
+
static ID redis_id;
|
26
|
+
static ID call_id;
|
56
27
|
|
57
28
|
/**
|
58
|
-
|
59
|
-
from
|
29
|
+
The {Iodine::PubSub::Engine} class is the parent for all engines to inherit
|
30
|
+
from.
|
60
31
|
|
61
|
-
|
62
|
-
|
32
|
+
Engines should inherit this class and override the `subscribe`, `unsubscribe`
|
33
|
+
and `publish` callbacks (which shall be called by {Iodine}).
|
63
34
|
|
64
|
-
|
35
|
+
After creation, Engines should attach themselves to Iodine using
|
36
|
+
{Iodine::PubSub.attach} or their callbacks will never get called.
|
37
|
+
|
38
|
+
Engines can also set themselves to be the default engine using
|
39
|
+
{Iodine::PubSub.default=}.
|
65
40
|
*/
|
66
|
-
static VALUE
|
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
|
-
}
|
79
|
-
return Qnil;
|
80
|
-
(void)self;
|
81
|
-
(void)msg;
|
82
|
-
(void)channel;
|
83
|
-
}
|
41
|
+
static VALUE EngineClass;
|
84
42
|
|
85
43
|
/* *****************************************************************************
|
86
|
-
|
44
|
+
Ruby <=> C Callbacks
|
87
45
|
***************************************************************************** */
|
88
46
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
(
|
104
|
-
|
47
|
+
typedef struct {
|
48
|
+
iodine_pubsub_s *eng;
|
49
|
+
FIOBJ ch;
|
50
|
+
FIOBJ msg;
|
51
|
+
uint8_t pattern;
|
52
|
+
} iodine_pubsub_task_s;
|
53
|
+
|
54
|
+
#define iodine_engine(eng) ((iodine_pubsub_s *)(eng))
|
55
|
+
|
56
|
+
/* calls an engine's `subscribe` callback within the GVL */
|
57
|
+
static void *iodine_pubsub_GIL_subscribe(void *tsk_) {
|
58
|
+
iodine_pubsub_task_s *task = tsk_;
|
59
|
+
VALUE args[2];
|
60
|
+
fio_cstr_s tmp = fiobj_obj2cstr(task->ch);
|
61
|
+
args[0] = rb_str_new(tmp.data, tmp.len);
|
62
|
+
args[1] = task->pattern ? Qtrue : Qnil; // TODO: Qtrue should be :redis
|
63
|
+
IodineCaller.call2(task->eng->handler, subscribe_id, 2, args);
|
64
|
+
return NULL;
|
105
65
|
}
|
106
66
|
|
107
|
-
/**
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
67
|
+
/** Must subscribe channel. Failures are ignored. */
|
68
|
+
static void iodine_pubsub_on_subscribe(const pubsub_engine_s *eng,
|
69
|
+
FIOBJ channel, uint8_t use_pattern) {
|
70
|
+
if (iodine_engine(eng)->handler == Qnil) {
|
71
|
+
return;
|
72
|
+
}
|
73
|
+
iodine_pubsub_task_s task = {
|
74
|
+
.eng = iodine_engine(eng), .ch = channel, .pattern = use_pattern};
|
75
|
+
IodineCaller.enterGVL(iodine_pubsub_GIL_subscribe, &task);
|
76
|
+
}
|
77
|
+
|
78
|
+
/* calls an engine's `unsubscribe` callback within the GVL */
|
79
|
+
static void *iodine_pubsub_GIL_unsubscribe(void *tsk_) {
|
80
|
+
iodine_pubsub_task_s *task = tsk_;
|
81
|
+
VALUE args[2];
|
82
|
+
fio_cstr_s tmp = fiobj_obj2cstr(task->ch);
|
83
|
+
args[0] = rb_str_new(tmp.data, tmp.len);
|
84
|
+
args[1] = task->pattern ? Qtrue : Qnil; // TODO: Qtrue should be :redis
|
85
|
+
IodineCaller.call2(task->eng->handler, unsubscribe_id, 2, args);
|
86
|
+
return NULL;
|
113
87
|
}
|
114
88
|
|
115
|
-
/**
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
89
|
+
/** Must unsubscribe channel. Failures are ignored. */
|
90
|
+
static void iodine_pubsub_on_unsubscribe(const pubsub_engine_s *eng,
|
91
|
+
FIOBJ channel, uint8_t use_pattern) {
|
92
|
+
if (iodine_engine(eng)->handler == Qnil) {
|
93
|
+
return;
|
94
|
+
}
|
95
|
+
iodine_pubsub_task_s task = {
|
96
|
+
.eng = iodine_engine(eng), .ch = channel, .pattern = use_pattern};
|
97
|
+
IodineCaller.enterGVL(iodine_pubsub_GIL_unsubscribe, &task);
|
98
|
+
}
|
99
|
+
|
100
|
+
/* calls an engine's `unsubscribe` callback within the GVL */
|
101
|
+
static void *iodine_pubsub_GIL_publish(void *tsk_) {
|
102
|
+
iodine_pubsub_task_s *task = tsk_;
|
103
|
+
VALUE args[2];
|
104
|
+
fio_cstr_s tmp = fiobj_obj2cstr(task->ch);
|
105
|
+
args[0] = rb_str_new(tmp.data, tmp.len);
|
106
|
+
tmp = fiobj_obj2cstr(task->msg);
|
107
|
+
args[1] = rb_str_new(tmp.data, tmp.len);
|
108
|
+
IodineCaller.call2(task->eng->handler, publish_id, 2, args);
|
109
|
+
return NULL;
|
130
110
|
}
|
131
111
|
|
132
|
-
/**
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
112
|
+
/** Should return 0 on success and -1 on failure. */
|
113
|
+
static int iodine_pubsub_on_publish(const pubsub_engine_s *eng, FIOBJ channel,
|
114
|
+
FIOBJ msg) {
|
115
|
+
if (iodine_engine(eng)->handler == Qnil) {
|
116
|
+
return -1;
|
117
|
+
}
|
118
|
+
iodine_pubsub_task_s task = {
|
119
|
+
.eng = iodine_engine(eng), .ch = channel, .msg = msg};
|
120
|
+
IodineCaller.enterGVL(iodine_pubsub_GIL_publish, &task);
|
121
|
+
return 0;
|
137
122
|
}
|
123
|
+
/**
|
124
|
+
* facil.io will call this callback whenever starting, or restarting, the
|
125
|
+
* reactor.
|
126
|
+
*
|
127
|
+
* but iodine engines should probably use the `before_fork` and `after_fork`
|
128
|
+
* hooks.
|
129
|
+
*/
|
130
|
+
static void iodine_pubsub_on_startup(const pubsub_engine_s *eng) { (void)eng; }
|
131
|
+
|
132
|
+
/* *****************************************************************************
|
133
|
+
Ruby methods
|
134
|
+
***************************************************************************** */
|
138
135
|
|
139
136
|
/**
|
140
|
-
|
141
|
-
|
137
|
+
OVERRIDE this callback - it will be called by {Iodine} whenever the process
|
138
|
+
CLUSTER (not just this process) subscribes to a stream / channel.
|
142
139
|
*/
|
143
|
-
static VALUE
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
return
|
140
|
+
static VALUE iodine_pubsub_subscribe(VALUE self, VALUE to, VALUE match) {
|
141
|
+
return Qnil;
|
142
|
+
#if 0
|
143
|
+
iodine_pubsub_s *e = iodine_pubsub_CData(self);
|
144
|
+
if (e->engine == &e->do_not_touch) {
|
145
|
+
/* this is a Ruby engine, nothing to do. */
|
146
|
+
return Qnil;
|
150
147
|
}
|
151
|
-
|
148
|
+
FIOBJ ch = fiobj_str_new(RSTRING_PTR(to), RSTRING_LEN(to));
|
149
|
+
e->engine->subscribe(e->engine, ch, SYM2ID(match) == redis_id);
|
150
|
+
fiobj_free(ch);
|
151
|
+
return to;
|
152
|
+
#endif
|
152
153
|
(void)self;
|
153
|
-
(void)
|
154
|
+
(void)to;
|
155
|
+
(void)match;
|
154
156
|
}
|
155
157
|
|
156
158
|
/**
|
157
|
-
|
158
|
-
|
159
|
+
OVERRIDE this callback - it will be called by {Iodine} whenever the whole
|
160
|
+
process CLUSTER (not just this process) unsubscribes from a stream / channel.
|
159
161
|
*/
|
160
|
-
static VALUE
|
161
|
-
return
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
Ruby
|
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;
|
162
|
+
static VALUE iodine_pubsub_unsubscribe(VALUE self, VALUE to, VALUE match) {
|
163
|
+
return Qnil;
|
164
|
+
#if 0
|
165
|
+
iodine_pubsub_s *e = iodine_pubsub_CData(self);
|
166
|
+
if (e->engine == &e->do_not_touch) {
|
167
|
+
/* this is a Ruby engine, nothing to do. */
|
168
|
+
return Qnil;
|
192
169
|
}
|
193
|
-
|
170
|
+
FIOBJ ch = fiobj_str_new(RSTRING_PTR(to), RSTRING_LEN(to));
|
171
|
+
e->engine->unsubscribe(e->engine, ch, SYM2ID(match) == redis_id);
|
172
|
+
fiobj_free(ch);
|
173
|
+
return to;
|
174
|
+
#endif
|
175
|
+
(void)self;
|
176
|
+
(void)to;
|
177
|
+
(void)match;
|
194
178
|
}
|
195
179
|
|
196
|
-
|
197
|
-
|
198
|
-
|
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));
|
207
|
-
return self;
|
208
|
-
}
|
180
|
+
/**
|
181
|
+
OVERRIDE this callback - it will be called by {Iodine} whenever the
|
182
|
+
{Iodine.publish} (or {Iodine::Connection#publish}) is called for this engine.
|
209
183
|
|
210
|
-
|
211
|
-
|
212
|
-
|
184
|
+
If this {Engine} is set as the default {Engine}, then any call to
|
185
|
+
{Iodine.publish} (or {Iodine::Connection#publish} will invoke this callback
|
186
|
+
(unless another {Engine} was specified).
|
213
187
|
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
188
|
+
NOTE: this callback is called per process event (not per cluster event) and the
|
189
|
+
{Engine} is responsible for message propagation.
|
190
|
+
*/
|
191
|
+
static VALUE iodine_pubsub_publish(VALUE self, VALUE to, VALUE message) {
|
192
|
+
iodine_pubsub_s *e = iodine_pubsub_CData(self);
|
193
|
+
if (e->engine == &e->do_not_touch) {
|
194
|
+
/* this is a Ruby engine, nothing to do. */
|
218
195
|
return Qnil;
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
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
|
-
|
248
|
-
pubsub_engine_s *iodine_engine_ruby2facil(VALUE ruby_engine) {
|
249
|
-
if (ruby_engine == Qnil || ruby_engine == Qfalse)
|
250
|
-
return NULL;
|
251
|
-
iodine_engine_s *engine;
|
252
|
-
Data_Get_Struct(ruby_engine, iodine_engine_s, engine);
|
253
|
-
if (engine)
|
254
|
-
return engine->p;
|
255
|
-
return NULL;
|
196
|
+
}
|
197
|
+
FIOBJ ch, msg;
|
198
|
+
ch = fiobj_str_new(RSTRING_PTR(to), RSTRING_LEN(to));
|
199
|
+
msg = fiobj_str_new(RSTRING_PTR(message), RSTRING_LEN(message));
|
200
|
+
e->engine->publish(e->engine, ch, msg);
|
201
|
+
fiobj_free(ch);
|
202
|
+
fiobj_free(msg);
|
203
|
+
return self;
|
204
|
+
(void)self;
|
205
|
+
(void)to;
|
206
|
+
(void)message;
|
256
207
|
}
|
257
208
|
|
258
209
|
/* *****************************************************************************
|
259
|
-
C
|
210
|
+
Ruby <=> C Data Type
|
260
211
|
***************************************************************************** */
|
261
212
|
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
}
|
268
|
-
|
269
|
-
static void *engine_subscribe_inGVL(void *a_) {
|
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;
|
274
|
-
VALUE data[2];
|
275
|
-
fio_cstr_s tmp = fiobj_obj2cstr(args->ch);
|
276
|
-
data[1] = args->use_pattern ? Qtrue : Qnil;
|
277
|
-
data[0] = rb_str_new(tmp.data, tmp.len);
|
278
|
-
eng = RubyCaller.call2(eng, engine_subid, 2, data);
|
279
|
-
return NULL;
|
280
|
-
}
|
281
|
-
|
282
|
-
/* Should return 0 on success and -1 on failure. */
|
283
|
-
static void engine_subscribe(const pubsub_engine_s *eng, FIOBJ ch,
|
284
|
-
uint8_t use_pattern) {
|
285
|
-
struct engine_gvl_args_s args = {
|
286
|
-
.eng = eng, .ch = ch, .use_pattern = use_pattern,
|
287
|
-
};
|
288
|
-
RubyCaller.call_c(engine_subscribe_inGVL, &args);
|
213
|
+
/* a callback for the GC (marking active objects) */
|
214
|
+
static void iodine_pubsub_data_mark(void *c_) {
|
215
|
+
iodine_pubsub_s *c = c_;
|
216
|
+
if (c->handler != Qnil) {
|
217
|
+
rb_gc_mark(c->handler);
|
218
|
+
}
|
289
219
|
}
|
290
|
-
|
291
|
-
static void
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
fio_cstr_s tmp = fiobj_obj2cstr(args->ch);
|
298
|
-
data[1] = args->use_pattern ? Qtrue : Qnil;
|
299
|
-
data[0] = rb_str_new(tmp.data, tmp.len);
|
300
|
-
RubyCaller.call2(eng, engine_unsubid, 2, data);
|
301
|
-
return NULL;
|
220
|
+
/* a callback for the GC (marking active objects) */
|
221
|
+
static void iodine_pubsub_data_free(void *c_) {
|
222
|
+
iodine_pubsub_s *data = c_;
|
223
|
+
if (data->dealloc) {
|
224
|
+
data->dealloc(data->engine);
|
225
|
+
}
|
226
|
+
free(data);
|
302
227
|
}
|
303
228
|
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
struct engine_gvl_args_s args = {
|
308
|
-
.eng = eng, .ch = ch, .use_pattern = use_pattern,
|
309
|
-
};
|
310
|
-
RubyCaller.call_c(engine_unsubscribe_inGVL, &args);
|
229
|
+
static size_t iodine_pubsub_data_size(const void *c_) {
|
230
|
+
return sizeof(iodine_pubsub_s);
|
231
|
+
(void)c_;
|
311
232
|
}
|
312
233
|
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
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]);
|
328
|
-
return ((eng == Qfalse || eng == Qnil) ? (void *)-1 : 0);
|
329
|
-
}
|
234
|
+
const rb_data_type_t iodine_pubsub_data_type = {
|
235
|
+
.wrap_struct_name = "IodinePubSubData",
|
236
|
+
.function =
|
237
|
+
{
|
238
|
+
.dmark = iodine_pubsub_data_mark,
|
239
|
+
.dfree = iodine_pubsub_data_free,
|
240
|
+
.dsize = iodine_pubsub_data_size,
|
241
|
+
},
|
242
|
+
.data = NULL,
|
243
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY,
|
244
|
+
};
|
330
245
|
|
331
|
-
/*
|
332
|
-
static
|
333
|
-
|
334
|
-
|
246
|
+
/* Iodine::PubSub::Engine.allocate */
|
247
|
+
static VALUE iodine_pubsub_data_alloc_c(VALUE self) {
|
248
|
+
iodine_pubsub_s *c = malloc(sizeof(*c));
|
249
|
+
*c = (iodine_pubsub_s){
|
250
|
+
.do_not_touch =
|
251
|
+
{
|
252
|
+
.subscribe = iodine_pubsub_on_subscribe,
|
253
|
+
.unsubscribe = iodine_pubsub_on_unsubscribe,
|
254
|
+
.publish = iodine_pubsub_on_publish,
|
255
|
+
.on_startup = iodine_pubsub_on_startup,
|
256
|
+
},
|
257
|
+
.handler = Qnil,
|
258
|
+
.engine = &c->do_not_touch,
|
335
259
|
};
|
336
|
-
return
|
260
|
+
return TypedData_Wrap_Struct(self, &iodine_pubsub_data_type, c);
|
337
261
|
}
|
338
262
|
|
339
263
|
/* *****************************************************************************
|
340
|
-
C
|
264
|
+
C engines
|
341
265
|
***************************************************************************** */
|
342
266
|
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
}
|
348
|
-
/* a callback for the GC (marking active objects) */
|
349
|
-
static void engine_free(void *eng_) {
|
350
|
-
iodine_engine_s *eng = eng_;
|
351
|
-
if (eng->dealloc)
|
352
|
-
eng->dealloc(eng->p);
|
353
|
-
free(eng);
|
354
|
-
}
|
355
|
-
|
356
|
-
/* Iodine::PubSub::Engine.allocate */
|
357
|
-
static VALUE engine_alloc_c(VALUE self) {
|
358
|
-
iodine_engine_s *eng = malloc(sizeof(*eng));
|
359
|
-
if (TYPE(self) == T_CLASS)
|
360
|
-
*eng = (iodine_engine_s){
|
361
|
-
.handler = (VALUE)0,
|
362
|
-
.engine =
|
363
|
-
{
|
364
|
-
.subscribe = engine_subscribe,
|
365
|
-
.unsubscribe = engine_unsubscribe,
|
366
|
-
.publish = engine_publish,
|
367
|
-
},
|
368
|
-
.p = &eng->engine,
|
369
|
-
};
|
370
|
-
|
371
|
-
return Data_Wrap_Struct(self, engine_mark, engine_free, eng);
|
372
|
-
}
|
373
|
-
|
374
|
-
static VALUE engine_initialize(VALUE self) {
|
375
|
-
iodine_engine_s *engine;
|
376
|
-
Data_Get_Struct(self, iodine_engine_s, engine);
|
377
|
-
if (TYPE(self) == T_CLASS) {
|
378
|
-
fprintf(stderr, "This sucks...\n");
|
267
|
+
static VALUE iodine_pubsub_make_C_engine(const pubsub_engine_s *e) {
|
268
|
+
VALUE engine = IodineCaller.call(EngineClass, rb_intern2("new", 3));
|
269
|
+
if (engine == Qnil) {
|
270
|
+
return Qnil;
|
379
271
|
}
|
380
|
-
engine->
|
381
|
-
return
|
272
|
+
iodine_pubsub_CData(engine)->engine = (pubsub_engine_s *)e;
|
273
|
+
return engine;
|
382
274
|
}
|
383
275
|
|
384
276
|
/* *****************************************************************************
|
385
|
-
|
277
|
+
PubSub module methods
|
386
278
|
***************************************************************************** */
|
387
279
|
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
/*
|
394
|
-
Perform a Redis message callback in the GVL
|
395
|
-
*/
|
396
|
-
static void *perform_redis_callback_inGVL(void *data) {
|
397
|
-
struct redis_callback_data *a = data;
|
398
|
-
VALUE reply = fiobj2rb_deep(a->msg, 1);
|
399
|
-
Registry.add(reply);
|
400
|
-
rb_funcallv(a->block, iodine_call_proc_id, 1, &reply);
|
401
|
-
Registry.remove(a->block);
|
402
|
-
Registry.remove(reply);
|
403
|
-
return NULL;
|
404
|
-
}
|
405
|
-
|
406
|
-
/*
|
407
|
-
Redis message callback
|
408
|
-
*/
|
409
|
-
static void redis_callback(pubsub_engine_s *e, FIOBJ reply, void *block) {
|
410
|
-
struct redis_callback_data d = {
|
411
|
-
.msg = reply, .block = (VALUE)block,
|
412
|
-
};
|
413
|
-
RubyCaller.call_c(perform_redis_callback_inGVL, &d);
|
414
|
-
(void)e;
|
415
|
-
}
|
416
|
-
|
417
|
-
/**
|
418
|
-
Sends commands / messages to the underlying Redis Pub connection.
|
419
|
-
|
420
|
-
The method accepts an optional callback block. i.e.:
|
421
|
-
|
422
|
-
redis.send("Echo", "Hello World!") do |reply|
|
423
|
-
p reply # => ["Hello World!"]
|
424
|
-
end
|
425
|
-
|
426
|
-
This connection is only for publishing and database commands. The Sub commands,
|
427
|
-
such as SUBSCRIBE and PSUBSCRIBE, will break the engine.
|
428
|
-
*/
|
429
|
-
static VALUE redis_send(int argc, VALUE *argv, VALUE self) {
|
430
|
-
if (argc < 1)
|
431
|
-
rb_raise(rb_eArgError,
|
432
|
-
"wrong number of arguments (given %d, expected at least 1).",
|
433
|
-
argc);
|
434
|
-
Check_Type(argv[0], T_STRING);
|
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])));
|
451
|
-
}
|
280
|
+
/** Sets the default {Iodine::PubSub::Engine} for pub/sub methods. */
|
281
|
+
static VALUE iodine_pubsub_default_set(VALUE self, VALUE engine) {
|
282
|
+
if (engine == Qnil) {
|
283
|
+
engine = rb_const_get(self, rb_intern2("CLUSTER", 7));
|
452
284
|
}
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
if (rb_block_given_p()) {
|
458
|
-
VALUE block = rb_block_proc();
|
459
|
-
Registry.add(block);
|
460
|
-
redis_engine_send(e->p, cmd, data, redis_callback, (void *)block);
|
461
|
-
return block;
|
462
|
-
} else {
|
463
|
-
redis_engine_send(e->p, cmd, data, NULL, NULL);
|
285
|
+
iodine_pubsub_s *e = iodine_pubsub_CData(engine);
|
286
|
+
if (!e) {
|
287
|
+
rb_raise(rb_eTypeError, "not a valid engine");
|
288
|
+
return Qnil;
|
464
289
|
}
|
465
|
-
|
466
|
-
|
467
|
-
|
290
|
+
if (e->handler == Qnil) {
|
291
|
+
e->handler = engine;
|
292
|
+
}
|
293
|
+
PUBSUB_DEFAULT_ENGINE = e->engine;
|
294
|
+
rb_ivar_set(self, rb_intern2("default_engine", 14), engine);
|
295
|
+
return engine;
|
468
296
|
}
|
469
297
|
|
470
|
-
/**
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
address:: the Redis server's address. Required.
|
480
|
-
port:: the Redis Server port. Default: 6379
|
481
|
-
ping:: the PING interval up to 255 seconds. Default: 0 (~5 minutes).
|
482
|
-
auth:: authentication password. Default: none.
|
483
|
-
*/
|
484
|
-
static VALUE redis_engine_initialize(int argc, VALUE *argv, VALUE self) {
|
485
|
-
if (argc < 1 || argc > 4)
|
486
|
-
rb_raise(rb_eArgError,
|
487
|
-
"wrong number of arguments (given %d, expected 1..4).", argc);
|
488
|
-
VALUE address = argv[0];
|
489
|
-
VALUE port = argc >= 2 ? argv[1] : Qnil;
|
490
|
-
VALUE ping = argc >= 3 ? argv[2] : Qnil;
|
491
|
-
VALUE auth = argc >= 4 ? argv[3] : Qnil;
|
492
|
-
Check_Type(address, T_STRING);
|
493
|
-
if (port != Qnil) {
|
494
|
-
if (TYPE(port) == T_FIXNUM)
|
495
|
-
port = rb_fix2str(port, 10);
|
496
|
-
Check_Type(port, T_STRING);
|
497
|
-
}
|
498
|
-
if (ping != Qnil)
|
499
|
-
Check_Type(ping, T_FIXNUM);
|
500
|
-
if (auth != Qnil) {
|
501
|
-
Check_Type(auth, T_STRING);
|
502
|
-
}
|
503
|
-
size_t iping = FIX2LONG(ping);
|
504
|
-
if (iping > 255)
|
505
|
-
rb_raise(rb_eRangeError, "ping_interval too big (0..255)");
|
506
|
-
|
507
|
-
iodine_engine_s *engine;
|
508
|
-
Data_Get_Struct(self, iodine_engine_s, engine);
|
509
|
-
engine->handler = self;
|
510
|
-
engine->p =
|
511
|
-
redis_engine_create(.address = StringValueCStr(address),
|
512
|
-
.port =
|
513
|
-
(port == Qnil ? "6379" : StringValueCStr(port)),
|
514
|
-
.ping_interval = iping,
|
515
|
-
.auth = (auth == Qnil ? NULL : StringValueCStr(auth)),
|
516
|
-
.auth_len = (auth == Qnil ? 0 : RSTRING_LEN(auth)));
|
517
|
-
engine->dealloc = redis_engine_destroy;
|
518
|
-
if (!engine->p)
|
519
|
-
rb_raise(rb_eRuntimeError, "unknown error, can't initialize RedisEngine.");
|
520
|
-
return self;
|
298
|
+
/** Returns the default {Iodine::PubSub::Engine} for pub/sub methods. */
|
299
|
+
static VALUE iodine_pubsub_default_get(VALUE self) {
|
300
|
+
VALUE def = rb_ivar_get(self, rb_intern2("default_engine", 14));
|
301
|
+
if (def == Qnil) {
|
302
|
+
def = rb_const_get(self, rb_intern2("CLUSTER", 7));
|
303
|
+
iodine_pubsub_default_set(self, def);
|
304
|
+
}
|
305
|
+
return def;
|
521
306
|
}
|
522
307
|
|
523
|
-
/* *****************************************************************************
|
524
|
-
PubSub settings
|
525
|
-
***************************************************************************** */
|
526
|
-
|
527
308
|
/**
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
if (!e
|
538
|
-
rb_raise(
|
539
|
-
|
540
|
-
|
541
|
-
|
309
|
+
* Attaches an {Iodine::PubSub::Engine} to the pub/sub system (more than a
|
310
|
+
* single engine can be attached at the same time).
|
311
|
+
*
|
312
|
+
* After an engine was attached, it's callbacks will be called
|
313
|
+
* ({Iodine::PubSub::Engine#subscribe} and {Iodine::PubSub::Engine#unsubscribe})
|
314
|
+
* in response to Pub/Sub events.
|
315
|
+
*/
|
316
|
+
static VALUE iodine_pubsub_attach(VALUE self, VALUE engine) {
|
317
|
+
iodine_pubsub_s *e = iodine_pubsub_CData(engine);
|
318
|
+
if (!e) {
|
319
|
+
rb_raise(rb_eTypeError, "not a valid engine");
|
320
|
+
return Qnil;
|
321
|
+
}
|
322
|
+
if (e->handler == Qnil) {
|
323
|
+
e->handler = engine;
|
324
|
+
}
|
325
|
+
IodineStore.add(engine);
|
326
|
+
pubsub_engine_register(e->engine);
|
327
|
+
return engine;
|
542
328
|
(void)self;
|
543
329
|
}
|
544
330
|
|
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);
|
550
|
-
}
|
551
|
-
|
552
331
|
/**
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
332
|
+
* Removes an {Iodine::PubSub::Engine} from the pub/sub system.
|
333
|
+
*
|
334
|
+
* After an {Iodine::PubSub::Engine} was detached, Iodine will no longer call
|
335
|
+
* the {Iodine::PubSub::Engine}'s callbacks ({Iodine::PubSub::Engine#subscribe}
|
336
|
+
* and {Iodine::PubSub::Engine#unsubscribe})
|
337
|
+
*/
|
338
|
+
static VALUE iodine_pubsub_dettach(VALUE self, VALUE engine) {
|
339
|
+
iodine_pubsub_s *e = iodine_pubsub_CData(engine);
|
340
|
+
if (!e) {
|
341
|
+
rb_raise(rb_eTypeError, "not a valid engine");
|
342
|
+
return Qnil;
|
343
|
+
}
|
344
|
+
if (e->handler == Qnil) {
|
345
|
+
e->handler = engine;
|
346
|
+
}
|
347
|
+
IodineStore.remove(engine);
|
348
|
+
pubsub_engine_deregister(e->engine);
|
349
|
+
return engine;
|
559
350
|
(void)self;
|
560
351
|
}
|
561
352
|
|
562
353
|
/**
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
354
|
+
* Forces {Iodine} to call the {Iodine::PubSub::Engine#subscribe} callback for
|
355
|
+
* all existing subscriptions (i.e., when reconnecting to a Pub/Sub backend such
|
356
|
+
* as Redis).
|
357
|
+
*/
|
358
|
+
static VALUE iodine_pubsub_reset(VALUE self, VALUE engine) {
|
359
|
+
iodine_pubsub_s *e = iodine_pubsub_CData(engine);
|
360
|
+
if (!e) {
|
361
|
+
rb_raise(rb_eTypeError, "not a valid engine");
|
362
|
+
return Qnil;
|
363
|
+
}
|
364
|
+
if (e->handler == Qnil) {
|
365
|
+
e->handler = engine;
|
366
|
+
}
|
367
|
+
pubsub_engine_resubscribe(e->engine);
|
368
|
+
return engine;
|
369
|
+
(void)self;
|
569
370
|
}
|
570
371
|
|
571
372
|
/* *****************************************************************************
|
572
|
-
|
373
|
+
Redis Engine
|
573
374
|
***************************************************************************** */
|
574
375
|
|
575
|
-
|
576
|
-
|
577
|
-
Registry.remove((VALUE)u1);
|
578
|
-
(void)u2;
|
579
|
-
}
|
376
|
+
/**
|
377
|
+
Initializes a new {Iodine::PubSub::Redis} engine.
|
580
378
|
|
581
|
-
|
582
|
-
VALUE rbn[2];
|
583
|
-
fio_cstr_s tmp = fiobj_obj2cstr(n->channel);
|
584
|
-
rbn[0] = rb_str_new(tmp.data, tmp.len);
|
585
|
-
Registry.add(rbn[0]);
|
586
|
-
tmp = fiobj_obj2cstr(n->message);
|
587
|
-
rbn[1] = rb_str_new(tmp.data, tmp.len);
|
588
|
-
Registry.add(rbn[1]);
|
589
|
-
RubyCaller.call2((VALUE)n->udata1, iodine_call_proc_id, 2, rbn);
|
590
|
-
Registry.remove(rbn[0]);
|
591
|
-
Registry.remove(rbn[1]);
|
592
|
-
return NULL;
|
593
|
-
}
|
379
|
+
Iodine::PubSub::Redis.new(url, opt = {})
|
594
380
|
|
595
|
-
|
596
|
-
RubyCaller.call_c((void *(*)(void *))on_pubsub_notificationinGVL, n);
|
597
|
-
}
|
381
|
+
use:
|
598
382
|
|
599
|
-
|
600
|
-
|
601
|
-
Registry.remove((VALUE)u);
|
602
|
-
}
|
383
|
+
REDIS_URL = "redis://localhost:6379/"
|
384
|
+
Iodine::PubSub::Redis.new(REDIS_URL, ping: 50) #pings every 50 seconds
|
603
385
|
|
604
|
-
|
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
|
-
}
|
386
|
+
To use Redis authentication, add the password to the URL. i.e.:
|
618
387
|
|
619
|
-
|
620
|
-
|
621
|
-
}
|
388
|
+
REDIS_URL = "redis://redis:password@localhost:6379/"
|
389
|
+
Iodine::PubSub::Redis.new(REDIS_URL, ping: 50) #pings every 50 seconds
|
622
390
|
|
623
|
-
|
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
|
-
}
|
391
|
+
The options hash accepts:
|
630
392
|
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
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.");
|
393
|
+
:ping:: the PING interval up to 255 seconds. Default: 0 (~5 minutes).
|
394
|
+
*/
|
395
|
+
static VALUE iodine_pubsub_redis_new(int argc, VALUE *argv, VALUE self) {
|
396
|
+
if (!argc) {
|
397
|
+
rb_raise(rb_eArgError, "Iodine::PubSub::Redis.new(address, opt={}) "
|
398
|
+
"requires at least 1 argument.");
|
399
|
+
}
|
400
|
+
VALUE url = argv[0];
|
401
|
+
Check_Type(url, T_STRING);
|
402
|
+
if (RSTRING_LEN(url) > 4096) {
|
403
|
+
rb_raise(rb_eArgError, "Redis URL too long.");
|
404
|
+
}
|
405
|
+
FIOBJ port = FIOBJ_INVALID;
|
406
|
+
FIOBJ address = FIOBJ_INVALID;
|
407
|
+
FIOBJ auth = FIOBJ_INVALID;
|
408
|
+
uint8_t ping = 0;
|
409
|
+
|
410
|
+
iodine_pubsub_s *e = iodine_pubsub_CData(self);
|
411
|
+
if (!e) {
|
412
|
+
rb_raise(rb_eTypeError, "not a valid engine");
|
665
413
|
return Qnil;
|
666
414
|
}
|
667
415
|
|
668
|
-
|
669
|
-
|
670
|
-
|
416
|
+
/* extract options */
|
417
|
+
if (argc == 2) {
|
418
|
+
Check_Type(argv[1], T_HASH);
|
419
|
+
VALUE tmp = rb_hash_aref(argv[1], rb_id2sym(rb_intern2("ping", 4)));
|
420
|
+
if (tmp != Qnil) {
|
421
|
+
Check_Type(tmp, T_FIXNUM);
|
422
|
+
if (NUM2SIZET(tmp) > 255) {
|
423
|
+
rb_raise(rb_eArgError,
|
424
|
+
":ping must be a non-negative integer under 255 seconds.");
|
425
|
+
}
|
426
|
+
ping = (uint8_t)NUM2SIZET(tmp);
|
427
|
+
}
|
671
428
|
}
|
672
429
|
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
430
|
+
/* parse URL assume redis://redis:password@localhost:6379 */
|
431
|
+
{
|
432
|
+
size_t l = RSTRING_LEN(url);
|
433
|
+
char *str = RSTRING_PTR(url);
|
434
|
+
char *pointers[5];
|
435
|
+
char *end = str + l;
|
436
|
+
uint8_t flag = 1;
|
437
|
+
uint8_t counter = 0;
|
438
|
+
for (size_t i = 0; i < l; i++) {
|
439
|
+
if (counter > 4)
|
440
|
+
goto finish;
|
441
|
+
if (str[i] == ':' && str[i + 1] == '/' && str[i + 2] == '/') {
|
442
|
+
pointers[counter++] = str + i + 3;
|
443
|
+
i = i + 2;
|
444
|
+
flag = 0;
|
445
|
+
continue;
|
446
|
+
}
|
447
|
+
if (str[i] == '@' && counter == 1 - flag) {
|
448
|
+
rb_raise(rb_eArgError, "malformed URL");
|
449
|
+
}
|
450
|
+
if (str[i] == ':' || str[i] == '@') {
|
451
|
+
pointers[counter++] = str + i + 1;
|
452
|
+
continue;
|
453
|
+
}
|
454
|
+
if (str[i] == '/') {
|
455
|
+
end = str + i;
|
456
|
+
break;
|
457
|
+
}
|
682
458
|
}
|
683
|
-
if (
|
684
|
-
|
459
|
+
if (flag) {
|
460
|
+
if (counter > 3) {
|
461
|
+
rb_raise(rb_eArgError, "malformed URL");
|
462
|
+
}
|
463
|
+
/* move pointers one step forward and set 0 to str... */
|
464
|
+
char *pointers_2[5];
|
465
|
+
for (size_t i = 0; i < counter; ++i) {
|
466
|
+
pointers_2[i + 1] = pointers[i];
|
467
|
+
}
|
468
|
+
pointers_2[0] = str;
|
469
|
+
++counter;
|
470
|
+
for (size_t i = 0; i < counter; ++i) {
|
471
|
+
pointers[i] = pointers_2[i];
|
472
|
+
}
|
473
|
+
}
|
474
|
+
/* review results */
|
475
|
+
switch (counter) {
|
476
|
+
case 1:
|
477
|
+
/* redis://localhost */
|
478
|
+
if (pointers[0] == end) {
|
479
|
+
goto finish;
|
480
|
+
}
|
481
|
+
address = fiobj_str_new(pointers[0], end - pointers[0]);
|
482
|
+
break;
|
483
|
+
case 2:
|
484
|
+
/* redis://localhost:6379 */
|
485
|
+
if (pointers[1] - pointers[0] - 1 == 0) {
|
486
|
+
goto finish;
|
487
|
+
}
|
488
|
+
address = fiobj_str_new(pointers[0], pointers[1] - pointers[0] - 1);
|
489
|
+
if (pointers[1] != end) {
|
490
|
+
port = fiobj_str_new(pointers[1], end - pointers[1]);
|
491
|
+
}
|
492
|
+
break;
|
493
|
+
case 3:
|
494
|
+
/* redis://redis:password@localhost */
|
495
|
+
if (pointers[2] - pointers[1] - 1 == 0 || end - pointers[2] == 0) {
|
496
|
+
goto finish;
|
497
|
+
}
|
498
|
+
address = fiobj_str_new(pointers[2], end - pointers[2]);
|
499
|
+
auth = fiobj_str_new(pointers[1], pointers[2] - pointers[1] - 1);
|
500
|
+
break;
|
501
|
+
case 4:
|
502
|
+
/* redis://redis:password@localhost:6379 */
|
503
|
+
if (pointers[2] - pointers[1] - 1 == 0 ||
|
504
|
+
pointers[3] - pointers[2] - 1 == 0 || end - pointers[3] == 0) {
|
505
|
+
goto finish;
|
506
|
+
}
|
507
|
+
port = fiobj_str_new(pointers[3], end - pointers[3]);
|
508
|
+
address = fiobj_str_new(pointers[2], pointers[3] - pointers[2] - 1);
|
509
|
+
auth = fiobj_str_new(pointers[1], pointers[2] - pointers[1] - 1);
|
510
|
+
break;
|
511
|
+
default:
|
512
|
+
goto finish;
|
685
513
|
}
|
686
|
-
|
687
|
-
|
688
|
-
|
514
|
+
}
|
515
|
+
fprintf(
|
516
|
+
stderr,
|
517
|
+
"INFO: Initializing Redis engine for address: %s - port: %s - auth %s\n",
|
518
|
+
fiobj_obj2cstr(address).data, fiobj_obj2cstr(port).data,
|
519
|
+
fiobj_obj2cstr(auth).data);
|
520
|
+
/* create engine */
|
521
|
+
e->engine = redis_engine_create(
|
522
|
+
.address = fiobj_obj2cstr(address)
|
523
|
+
.data,
|
524
|
+
.port = (port == FIOBJ_INVALID ? "6379" : fiobj_obj2cstr(port).data),
|
525
|
+
.ping_interval = ping,
|
526
|
+
.auth = (auth == FIOBJ_INVALID ? NULL : fiobj_obj2cstr(auth).data),
|
527
|
+
.auth_len = (auth == FIOBJ_INVALID ? 0 : fiobj_obj2cstr(auth).len));
|
528
|
+
if (!e->engine) {
|
529
|
+
e->engine = &e->do_not_touch;
|
530
|
+
} else {
|
531
|
+
e->dealloc = redis_engine_destroy;
|
689
532
|
}
|
690
533
|
|
534
|
+
finish:
|
535
|
+
fiobj_free(port);
|
536
|
+
fiobj_free(address);
|
537
|
+
fiobj_free(auth);
|
538
|
+
if (e->engine == &e->do_not_touch) {
|
539
|
+
rb_raise(rb_eArgError,
|
540
|
+
"Error initializing the Redis engine - malformed URL?");
|
541
|
+
}
|
542
|
+
return self;
|
543
|
+
(void)self;
|
544
|
+
(void)argc;
|
545
|
+
(void)argv;
|
546
|
+
}
|
547
|
+
|
548
|
+
/** A callback for Redis commands. */
|
549
|
+
static void iodine_pubsub_redis_callback(pubsub_engine_s *e, FIOBJ response,
|
550
|
+
void *udata) {
|
551
|
+
VALUE block = (VALUE)udata;
|
691
552
|
if (block == Qnil) {
|
692
|
-
|
693
|
-
block = rb_block_proc();
|
694
|
-
Registry.add(block);
|
695
|
-
} else if (type == IODINE_PUBSUB_GLOBAL) {
|
696
|
-
rb_need_block();
|
697
|
-
return Qnil;
|
698
|
-
}
|
553
|
+
return;
|
699
554
|
}
|
700
|
-
|
701
|
-
|
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);
|
729
|
-
|
730
|
-
break;
|
555
|
+
VALUE rb = Qnil;
|
556
|
+
if (!FIOBJ_IS_NULL(response)) {
|
557
|
+
rb = IodineStore.add(fiobj2rb_deep(response, 0));
|
731
558
|
}
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
return subscription_initialize(sub, uuid, owner, type, rb_ch);
|
559
|
+
IodineCaller.call2(block, call_id, 1, &rb);
|
560
|
+
IodineStore.remove(rb);
|
561
|
+
IodineStore.remove(block);
|
562
|
+
(void)e;
|
737
563
|
}
|
738
564
|
|
739
565
|
// clang-format off
|
740
566
|
/**
|
741
|
-
|
567
|
+
Sends a Redis command. Accepts an optional block that will recieve the response.
|
742
568
|
|
743
|
-
|
744
|
-
to call the method:
|
569
|
+
i.e.:
|
745
570
|
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
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
|
-
|
767
|
-
*/
|
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);
|
771
|
-
(void)self;
|
571
|
+
REDIS_URL = "redis://redis:password@localhost:6379/"
|
572
|
+
redis = Iodine::PubSub::Redis.new(REDIS_URL, ping: 50) #pings every 50 seconds
|
573
|
+
Iodine::PubSub.default = redis
|
574
|
+
redis.cmd("KEYS", "*") {|result| p result
|
772
575
|
}
|
773
576
|
|
774
|
-
/**
|
775
|
-
Publishes a message to a channel.
|
776
|
-
|
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)
|
784
|
-
|
785
|
-
|
786
|
-
Alternatively, accepts the following named arguments:
|
787
|
-
|
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.
|
794
577
|
|
795
578
|
*/
|
796
|
-
VALUE
|
797
|
-
|
798
|
-
|
799
|
-
|
800
|
-
|
801
|
-
/* fallthrough */
|
802
|
-
rb_engine = argv[2];
|
803
|
-
case 2:
|
804
|
-
rb_ch = argv[0];
|
805
|
-
rb_msg = argv[1];
|
806
|
-
break;
|
807
|
-
case 1: {
|
808
|
-
/* single argument must be a Hash */
|
809
|
-
Check_Type(argv[0], T_HASH);
|
810
|
-
rb_ch = rb_hash_aref(argv[0], to_sym_id);
|
811
|
-
if (rb_ch == Qnil || rb_ch == Qfalse) {
|
812
|
-
rb_ch = rb_hash_aref(argv[0], channel_sym_id);
|
813
|
-
}
|
814
|
-
rb_msg = rb_hash_aref(argv[0], message_sym_id);
|
815
|
-
rb_engine = rb_hash_aref(argv[0], engine_varid);
|
816
|
-
} break;
|
817
|
-
default:
|
818
|
-
rb_raise(rb_eArgError, "method accepts 1-3 arguments.");
|
579
|
+
static VALUE iodine_pubsub_redis_cmd(int argc, VALUE *argv, VALUE self) {
|
580
|
+
// clang-format on
|
581
|
+
if (argc <= 0) {
|
582
|
+
rb_raise(rb_eArgError, "Iodine::PubSub::Redis#cmd(command, ...) is missing "
|
583
|
+
"the required command argument.");
|
819
584
|
}
|
820
|
-
|
821
|
-
if (
|
822
|
-
rb_raise(
|
585
|
+
iodine_pubsub_s *e = iodine_pubsub_CData(self);
|
586
|
+
if (!e || !e->engine || e->engine == &e->do_not_touch) {
|
587
|
+
rb_raise(rb_eTypeError,
|
588
|
+
"Iodine::PubSub::Redis internal error - obsolete object?");
|
823
589
|
}
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
rb_raise(rb_eArgError, "target / channel is required .");
|
828
|
-
if (TYPE(rb_ch) == T_SYMBOL)
|
829
|
-
rb_ch = rb_sym2str(rb_ch);
|
830
|
-
Check_Type(rb_ch, T_STRING);
|
831
|
-
|
832
|
-
if (rb_engine == Qfalse) {
|
833
|
-
engine = PUBSUB_PROCESS_ENGINE;
|
834
|
-
} else if (rb_engine == Qnil) {
|
835
|
-
engine = NULL;
|
836
|
-
} else {
|
837
|
-
engine = iodine_engine_ruby2facil(rb_engine);
|
590
|
+
VALUE block = Qnil;
|
591
|
+
if (rb_block_given_p()) {
|
592
|
+
block = IodineStore.add(rb_block_proc());
|
838
593
|
}
|
594
|
+
FIOBJ data = fiobj_ary_new2((size_t)argc);
|
595
|
+
for (int i = 0; i < argc; ++i) {
|
596
|
+
switch (TYPE(argv[i])) {
|
597
|
+
case T_SYMBOL:
|
598
|
+
argv[i] = rb_sym2str(argv[i]);
|
599
|
+
/* overflow */
|
600
|
+
case T_STRING:
|
601
|
+
fiobj_ary_push(data,
|
602
|
+
fiobj_str_new(RSTRING_PTR(argv[i]), RSTRING_LEN(argv[i])));
|
603
|
+
break;
|
604
|
+
case T_FIXNUM:
|
605
|
+
fiobj_ary_push(data, fiobj_num_new(NUM2SSIZET(argv[i])));
|
606
|
+
break;
|
607
|
+
case T_FLOAT:
|
608
|
+
fiobj_ary_push(data, fiobj_float_new(rb_float_value(argv[i])));
|
609
|
+
break;
|
610
|
+
case T_NIL:
|
611
|
+
fiobj_ary_push(data, fiobj_null());
|
612
|
+
break;
|
613
|
+
case T_TRUE:
|
614
|
+
fiobj_ary_push(data, fiobj_true());
|
615
|
+
break;
|
616
|
+
case T_FALSE:
|
617
|
+
fiobj_ary_push(data, fiobj_false());
|
618
|
+
break;
|
619
|
+
default:
|
620
|
+
goto wrong_type;
|
621
|
+
}
|
622
|
+
}
|
623
|
+
FIOBJ cmd = fiobj_ary_shift(data);
|
624
|
+
if (redis_engine_send(e->engine, cmd, data, iodine_pubsub_redis_callback,
|
625
|
+
(void *)block)) {
|
626
|
+
iodine_pubsub_redis_callback(e->engine, fiobj_null(), (void *)block);
|
627
|
+
}
|
628
|
+
fiobj_free(cmd);
|
629
|
+
fiobj_free(data);
|
630
|
+
return self;
|
839
631
|
|
840
|
-
|
841
|
-
|
842
|
-
|
843
|
-
|
844
|
-
|
845
|
-
fiobj_free(ch);
|
846
|
-
fiobj_free(msg);
|
847
|
-
if (!ret)
|
848
|
-
return Qfalse;
|
849
|
-
return Qtrue;
|
850
|
-
(void)self;
|
632
|
+
wrong_type:
|
633
|
+
fiobj_free(data);
|
634
|
+
rb_raise(rb_eArgError,
|
635
|
+
"only String, Number (with limits), Symbol, true, false and nil "
|
636
|
+
"arguments can be used.");
|
851
637
|
}
|
852
638
|
|
853
639
|
/* *****************************************************************************
|
854
|
-
|
640
|
+
Module initialization
|
855
641
|
***************************************************************************** */
|
856
|
-
void Iodine_init_pubsub(void) {
|
857
|
-
default_pubsubid = rb_intern("default_pubsub");
|
858
|
-
engine_subid = rb_intern("subscribe");
|
859
|
-
engine_unsubid = rb_intern("unsubscribe");
|
860
|
-
engine_varid = rb_intern("engine");
|
861
|
-
iodine_engine_pubid = rb_intern("publish");
|
862
|
-
to_str_shadow_id = rb_intern("@to_s");
|
863
|
-
|
864
|
-
as_sym_id = ID2SYM(rb_intern("as"));
|
865
|
-
binary_sym_id = ID2SYM(rb_intern("binary"));
|
866
|
-
handler_sym_id = ID2SYM(rb_intern("handler"));
|
867
|
-
match_sym_id = ID2SYM(rb_intern("match"));
|
868
|
-
message_sym_id = ID2SYM(rb_intern("message"));
|
869
|
-
redis_sym_id = ID2SYM(rb_intern("redis"));
|
870
|
-
text_sym_id = ID2SYM(rb_intern("text"));
|
871
|
-
to_sym_id = ID2SYM(rb_intern("to"));
|
872
|
-
|
873
|
-
channel_sym_id = ID2SYM(rb_intern("channel")); /* bawards compatibility */
|
874
|
-
|
875
|
-
IodinePubSub = rb_define_module_under(Iodine, "PubSub");
|
876
|
-
IodineEngine = rb_define_class_under(IodinePubSub, "Engine", rb_cObject);
|
877
|
-
IodinePubSubSubscription =
|
878
|
-
rb_define_class_under(IodinePubSub, "Subscription", rb_cObject);
|
879
|
-
|
880
|
-
rb_define_method(IodinePubSubSubscription, "close", close_subscription, 0);
|
881
|
-
rb_define_method(IodinePubSubSubscription, "==", subscription_eq_s, 1);
|
882
|
-
rb_attr(IodinePubSubSubscription, rb_intern("to_s"), 1, 0, 1);
|
883
|
-
rb_define_method(IodinePubSubSubscription, "to_s", subscription_to_s, 1);
|
884
|
-
|
885
|
-
rb_define_alloc_func(IodineEngine, engine_alloc_c);
|
886
|
-
rb_define_method(IodineEngine, "initialize", engine_initialize, 0);
|
887
|
-
|
888
|
-
rb_define_method(IodineEngine, "subscribe", engine_sub_placeholder, 2);
|
889
|
-
rb_define_method(IodineEngine, "unsubscribe", engine_sub_placeholder, 2);
|
890
|
-
rb_define_method(IodineEngine, "publish", engine_pub_placeholder, 2);
|
891
|
-
rb_define_method(IodineEngine, "register", iodine_engine_register, 0);
|
892
|
-
rb_define_method(IodineEngine, "deregister", iodine_engine_deregister, 0);
|
893
|
-
rb_define_method(IodineEngine, "reset", iodine_engine_reset, 0);
|
894
|
-
|
895
|
-
rb_define_module_function(Iodine, "subscribe", iodine_subscribe_global, -1);
|
896
|
-
rb_define_module_function(Iodine, "publish", iodine_publish, -1);
|
897
|
-
rb_define_module_function(Iodine, "default_engine=", ips_set_default, 1);
|
898
|
-
rb_define_module_function(Iodine, "default_engine", ips_get_default, 0);
|
899
|
-
|
900
|
-
rb_define_module_function(IodinePubSub, "default_engine=", ips_set_default,
|
901
|
-
1);
|
902
|
-
rb_define_module_function(IodinePubSub, "default_engine", ips_get_default, 0);
|
903
|
-
rb_define_method(IodinePubSub, "register", iodine_engine_register2, 1);
|
904
|
-
rb_define_method(IodinePubSub, "deregister", iodine_engine_deregister2, 1);
|
905
|
-
rb_define_method(IodinePubSub, "reset", iodine_engine_reset2, 1);
|
906
|
-
|
907
|
-
rb_define_module_function(IodinePubSub, "subscribe", iodine_subscribe_global,
|
908
|
-
-1);
|
909
|
-
rb_define_module_function(IodinePubSub, "publish", iodine_publish, -1);
|
910
642
|
|
911
|
-
|
643
|
+
/** Initializes the Connection Ruby class. */
|
644
|
+
void iodine_pubsub_init(void) {
|
645
|
+
subscribe_id = rb_intern2("subscribe", 9);
|
646
|
+
unsubscribe_id = rb_intern2("unsubscribe", 11);
|
647
|
+
publish_id = rb_intern2("publish", 7);
|
648
|
+
default_id = rb_intern2("default_engine", 14);
|
649
|
+
redis_id = rb_intern2("redis", 5);
|
650
|
+
call_id = rb_intern2("call", 4);
|
912
651
|
|
913
|
-
|
914
|
-
rb_define_module_function(Iodine, "default_pubsub", ips_get_default_dep, 0);
|
652
|
+
/* Define the PubSub module and it's methods */
|
915
653
|
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
|
920
|
-
|
921
|
-
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
|
935
|
-
|
936
|
-
|
937
|
-
|
938
|
-
|
939
|
-
|
940
|
-
|
941
|
-
|
942
|
-
|
943
|
-
|
944
|
-
rb_define_method(
|
945
|
-
|
654
|
+
VALUE PubSubModule = rb_define_module_under(IodineModule, "PubSub");
|
655
|
+
rb_define_module_function(PubSubModule, "default=", iodine_pubsub_default_set,
|
656
|
+
1);
|
657
|
+
rb_define_module_function(PubSubModule, "default", iodine_pubsub_default_get,
|
658
|
+
0);
|
659
|
+
rb_define_module_function(PubSubModule, "attach", iodine_pubsub_attach, 1);
|
660
|
+
rb_define_module_function(PubSubModule, "dettach", iodine_pubsub_dettach, 1);
|
661
|
+
rb_define_module_function(PubSubModule, "reset", iodine_pubsub_reset, 1);
|
662
|
+
|
663
|
+
/* Define the Engine class and it's methods */
|
664
|
+
|
665
|
+
/**
|
666
|
+
The {Iodine::PubSub::Engine} class is the parent for all engines to inherit
|
667
|
+
from.
|
668
|
+
|
669
|
+
Engines should inherit this class and override the `subscribe`, `unsubscribe`
|
670
|
+
and `publish` callbacks (which shall be called by {Iodine}).
|
671
|
+
|
672
|
+
After creation, Engines should attach themselves to Iodine using
|
673
|
+
{Iodine::PubSub.attach} or their callbacks will never get called.
|
674
|
+
|
675
|
+
Engines can also set themselves to be the default engine using
|
676
|
+
{Iodine::PubSub.default=}.
|
677
|
+
*/
|
678
|
+
EngineClass = rb_define_class_under(PubSubModule, "Engine", rb_cObject);
|
679
|
+
rb_define_alloc_func(EngineClass, iodine_pubsub_data_alloc_c);
|
680
|
+
rb_define_method(EngineClass, "subscribe", iodine_pubsub_subscribe, 2);
|
681
|
+
rb_define_method(EngineClass, "unsubscribe", iodine_pubsub_unsubscribe, 2);
|
682
|
+
rb_define_method(EngineClass, "publish", iodine_pubsub_publish, 2);
|
683
|
+
|
684
|
+
/* Define the CLUSTER and PROCESS engines */
|
685
|
+
|
686
|
+
/* CLUSTER publishes data to all the subscribers in the process cluster. */
|
687
|
+
rb_define_const(PubSubModule, "CLUSTER",
|
688
|
+
iodine_pubsub_make_C_engine(PUBSUB_CLUSTER_ENGINE));
|
689
|
+
/* PROCESS publishes data to all the subscribers in a single process. */
|
690
|
+
rb_define_const(PubSubModule, "PROCESS",
|
691
|
+
iodine_pubsub_make_C_engine(PUBSUB_PROCESS_ENGINE));
|
692
|
+
|
693
|
+
VALUE RedisClass = rb_define_class_under(PubSubModule, "Redis", EngineClass);
|
694
|
+
rb_define_method(RedisClass, "initialize", iodine_pubsub_redis_new, -1);
|
695
|
+
rb_define_method(RedisClass, "cmd", iodine_pubsub_redis_cmd, -1);
|
946
696
|
}
|