statsrb 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/ext/statsrb/extconf.rb +3 -0
- data/ext/statsrb/statsrb.c +450 -0
- data/lib/statsrb.rb +37 -0
- metadata +49 -0
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
#include <ruby.h>
|
|
2
|
+
#include <stdio.h>
|
|
3
|
+
#include <string.h>
|
|
4
|
+
#include <stdlib.h>
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Loads a file and filters on a specified namespace.
|
|
8
|
+
*/
|
|
9
|
+
static VALUE statsrb_query(VALUE self, VALUE logfile, VALUE query_ns, VALUE query_limit, VALUE query_start, VALUE query_end) {
|
|
10
|
+
FILE * file;
|
|
11
|
+
int line_size = 256;
|
|
12
|
+
char *line = (char *) malloc(line_size);
|
|
13
|
+
const char *filepath = RSTRING_PTR(logfile);
|
|
14
|
+
const char *query_ns_char = RSTRING_PTR(query_ns);
|
|
15
|
+
|
|
16
|
+
// @data hash key symbols.
|
|
17
|
+
VALUE statsrb_key_ts = rb_iv_get(self, "@key_ts");
|
|
18
|
+
VALUE statsrb_key_ns = rb_iv_get(self, "@key_ns");
|
|
19
|
+
VALUE statsrb_key_v = rb_iv_get(self, "@key_v");
|
|
20
|
+
// Create an empty string for comparison.
|
|
21
|
+
VALUE statsrb_str_empty = rb_str_new2("");
|
|
22
|
+
|
|
23
|
+
// Convert into an int that ruby understands.
|
|
24
|
+
int limit = NUM2INT(query_limit);
|
|
25
|
+
int qstart = NUM2INT(query_start);
|
|
26
|
+
int qend = NUM2INT(query_end);
|
|
27
|
+
|
|
28
|
+
// Return array instantiation.
|
|
29
|
+
VALUE statsrb_data = rb_iv_get(self, "@data");
|
|
30
|
+
// @TODO does this garbage collect all of the old hash data?
|
|
31
|
+
rb_ary_resize(statsrb_data, 0);
|
|
32
|
+
|
|
33
|
+
file = fopen(filepath, "r");
|
|
34
|
+
if (file == NULL) {
|
|
35
|
+
fprintf(stderr, "File error: could not open file %s for reading.", filepath);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
int count = 0;
|
|
40
|
+
|
|
41
|
+
while (NULL != fgets(line, line_size, file) && count < limit) {
|
|
42
|
+
// strstr doesn't work with newline chars.
|
|
43
|
+
size_t len = strlen(line) - 1;
|
|
44
|
+
if (line[len] == '\n');
|
|
45
|
+
line[len] = '\0';
|
|
46
|
+
|
|
47
|
+
// If the namespace is in the row, explode it.
|
|
48
|
+
if (line[0] != '\0' && line[0] != '\n' && strchr(line, query_ns_char[0]) && strstr(line, query_ns_char)) {
|
|
49
|
+
VALUE statsrb_event = rb_hash_new();
|
|
50
|
+
|
|
51
|
+
// I tried sscanf for convenience, but it was predictably slower.
|
|
52
|
+
//int statsrb_ts, statsrb_v;
|
|
53
|
+
//sscanf(line, "%d\t%*s\t%d", &statsrb_ts, &statsrb_v);
|
|
54
|
+
|
|
55
|
+
// @TODO this should something more robust than atoi.
|
|
56
|
+
int statsrb_ts = atoi(strtok(line, "\t"));
|
|
57
|
+
|
|
58
|
+
if (statsrb_ts != NULL && (qstart == 0 || statsrb_ts >= qstart) && (qend == 0 || statsrb_ts <= qend)) {
|
|
59
|
+
// @TODO this should probably use the actual namespace if we do wildcard queries.
|
|
60
|
+
VALUE statsrb_str_ns = rb_str_new2(strtok(NULL, "\t"));
|
|
61
|
+
//strtok(NULL, "\t");
|
|
62
|
+
int statsrb_v = atoi(strtok(NULL, "\0"));
|
|
63
|
+
|
|
64
|
+
// @TODO this should really query the namespace exactly instead of just relying on strstr.
|
|
65
|
+
//if (rb_str_cmp(query_ns, statsrb_str_empty) == 0 || rb_str_cmp(query_ns, statsrb_str_ns) == 0) {
|
|
66
|
+
if (statsrb_ts && (statsrb_v || statsrb_v == 0)) {
|
|
67
|
+
rb_hash_aset(statsrb_event, statsrb_key_ts, INT2NUM(statsrb_ts));
|
|
68
|
+
rb_hash_aset(statsrb_event, statsrb_key_ns, statsrb_str_ns);
|
|
69
|
+
//rb_hash_aset(statsrb_event, statsrb_key_ns, query_ns);
|
|
70
|
+
rb_hash_aset(statsrb_event, statsrb_key_v, INT2NUM(statsrb_v));
|
|
71
|
+
rb_ary_push(statsrb_data, statsrb_event);
|
|
72
|
+
count++;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// terminate
|
|
79
|
+
fclose (file);
|
|
80
|
+
free (line);
|
|
81
|
+
|
|
82
|
+
//return statsrb_data;
|
|
83
|
+
//rb_iv_set(self, "@data", statsrb_data);
|
|
84
|
+
|
|
85
|
+
return self;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Implementation of quicksort algorithm.
|
|
90
|
+
*/
|
|
91
|
+
void time_sort(int left, int right, VALUE ary, VALUE statsrb_key_ts) {
|
|
92
|
+
int i = left;
|
|
93
|
+
int j = right;
|
|
94
|
+
int p = (i + j) / 2;
|
|
95
|
+
int pv = NUM2INT(rb_hash_aref(rb_ary_entry(ary, p), statsrb_key_ts));
|
|
96
|
+
VALUE tmp;
|
|
97
|
+
|
|
98
|
+
while (i <= j) {
|
|
99
|
+
while (NUM2INT(rb_hash_aref(rb_ary_entry(ary, i), statsrb_key_ts)) < pv) {
|
|
100
|
+
i++;
|
|
101
|
+
}
|
|
102
|
+
while (NUM2INT(rb_hash_aref(rb_ary_entry(ary, j), statsrb_key_ts)) > pv) {
|
|
103
|
+
j--;
|
|
104
|
+
}
|
|
105
|
+
if (i <= j) {
|
|
106
|
+
tmp = rb_ary_entry(ary, i);
|
|
107
|
+
rb_ary_store(ary, i, rb_ary_entry(ary, j));
|
|
108
|
+
rb_ary_store(ary, j, tmp);
|
|
109
|
+
i++;
|
|
110
|
+
j--;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (left < j) {
|
|
115
|
+
time_sort(left, j, ary, statsrb_key_ts);
|
|
116
|
+
}
|
|
117
|
+
if (i < right) {
|
|
118
|
+
time_sort(i, right, ary, statsrb_key_ts);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Sort the internal data using a quicksort algorithm based on the hash element's timestamp.
|
|
124
|
+
*/
|
|
125
|
+
static VALUE statsrb_sort(VALUE self) {
|
|
126
|
+
VALUE statsrb_data = rb_iv_get(self, "@data");
|
|
127
|
+
int len = RARRAY_LEN(statsrb_data);
|
|
128
|
+
if (len > 0) {
|
|
129
|
+
VALUE statsrb_key_ts = rb_iv_get(self, "@key_ts");
|
|
130
|
+
time_sort(0, len - 1, statsrb_data, statsrb_key_ts);
|
|
131
|
+
}
|
|
132
|
+
return statsrb_data;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Write the in-memory data to a file.
|
|
137
|
+
*/
|
|
138
|
+
static VALUE statsrb_write(VALUE self, VALUE logfile, VALUE mode) {
|
|
139
|
+
FILE * file;
|
|
140
|
+
const char *filepath = RSTRING_PTR(logfile);
|
|
141
|
+
const char *filemode = RSTRING_PTR(mode);
|
|
142
|
+
VALUE statsrb_data = rb_iv_get(self, "@data");
|
|
143
|
+
int data_length = RARRAY_LEN(statsrb_data);
|
|
144
|
+
int i;
|
|
145
|
+
int line_size = 256;
|
|
146
|
+
int tmp_ts, tmp_v;
|
|
147
|
+
const char *tmp_ns = (char *) malloc(line_size);
|
|
148
|
+
|
|
149
|
+
// @data hash key symbols.
|
|
150
|
+
VALUE statsrb_key_ts = rb_iv_get(self, "@key_ts");
|
|
151
|
+
VALUE statsrb_key_ns = rb_iv_get(self, "@key_ns");
|
|
152
|
+
VALUE statsrb_key_v = rb_iv_get(self, "@key_v");
|
|
153
|
+
|
|
154
|
+
file = fopen(filepath, filemode);
|
|
155
|
+
if (file==NULL) {
|
|
156
|
+
fprintf(stderr, "File error: could not open file %s mode %s.", filepath, filemode);
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Iterate through the data array, writing the data as we go.
|
|
161
|
+
for (i = 0; i < data_length; i++) {
|
|
162
|
+
// @TODO make sure that these values are not empty before writing.
|
|
163
|
+
//VALUE tmp_line = rb_str_tmp_new(line_size);
|
|
164
|
+
tmp_ts = NUM2INT(rb_hash_aref(rb_ary_entry(statsrb_data, i), statsrb_key_ts));
|
|
165
|
+
tmp_ns = RSTRING_PTR(rb_hash_aref(rb_ary_entry(statsrb_data, i), statsrb_key_ns));
|
|
166
|
+
tmp_v = NUM2INT(rb_hash_aref(rb_ary_entry(statsrb_data, i), statsrb_key_v));
|
|
167
|
+
fprintf(file, "%d\t%s\t%d\n", tmp_ts, tmp_ns, tmp_v);
|
|
168
|
+
//rb_str_free(tmp_line);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
fclose (file);
|
|
172
|
+
return self;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* A method to split unique namespaces from internal memory and write them to individual files.
|
|
177
|
+
*/
|
|
178
|
+
static VALUE statsrb_split_write(VALUE self, VALUE logdir, VALUE mode) {
|
|
179
|
+
VALUE statsrb_data = rb_iv_get(self, "@data");
|
|
180
|
+
int len = RARRAY_LEN(statsrb_data);
|
|
181
|
+
int i, ii, ns_len;
|
|
182
|
+
|
|
183
|
+
// @data hash key symbols.
|
|
184
|
+
VALUE statsrb_key_ts = rb_iv_get(self, "@key_ts");
|
|
185
|
+
VALUE statsrb_key_ns = rb_iv_get(self, "@key_ns");
|
|
186
|
+
VALUE statsrb_key_v = rb_iv_get(self, "@key_v");
|
|
187
|
+
|
|
188
|
+
VALUE ns_list = rb_ary_new();
|
|
189
|
+
|
|
190
|
+
for (i = 0; i < len; i++) {
|
|
191
|
+
if (!rb_ary_includes(ns_list, rb_hash_aref(rb_ary_entry(statsrb_data, i), statsrb_key_ns))) {
|
|
192
|
+
rb_ary_push(ns_list, rb_hash_aref(rb_ary_entry(statsrb_data, i), statsrb_key_ns));
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
ns_len = RARRAY_LEN(ns_list);
|
|
197
|
+
|
|
198
|
+
for (i = 0; i < ns_len; i++) {
|
|
199
|
+
VALUE tmp = rb_obj_dup(self);
|
|
200
|
+
VALUE tmp_data = rb_ary_new();
|
|
201
|
+
for (ii = 0; ii < len; ii++) {
|
|
202
|
+
if (rb_str_cmp(rb_ary_entry(ns_list, i), rb_hash_aref(rb_ary_entry(statsrb_data, ii), statsrb_key_ns)) == 0) {
|
|
203
|
+
rb_ary_push(tmp_data, rb_ary_entry(statsrb_data, ii));
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
//fputs (RSTRING_PTR(rb_obj_as_string(INT2NUM(RARRAY_LEN(tmp_data)))),stderr);
|
|
207
|
+
rb_iv_set(tmp, "@data", tmp_data);
|
|
208
|
+
|
|
209
|
+
// @todo, throw an exception if no trailing slash... or add one
|
|
210
|
+
statsrb_write(tmp, rb_str_plus(logdir, rb_ary_entry(ns_list, i)), mode);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return self;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Parses the query string parameters.
|
|
218
|
+
*
|
|
219
|
+
* @param char * qs
|
|
220
|
+
* The location of the query string.
|
|
221
|
+
*
|
|
222
|
+
* @return VALUE
|
|
223
|
+
* The ruby hash containing the query string keys and values.
|
|
224
|
+
*/
|
|
225
|
+
static VALUE statsrb_parse_qs(char *qs) {
|
|
226
|
+
char *qsk, *qsv;
|
|
227
|
+
VALUE query_string_tmp = rb_ary_new();
|
|
228
|
+
VALUE query_string = rb_hash_new();
|
|
229
|
+
qsk = strtok(qs, "&\0");
|
|
230
|
+
while (qsk != NULL) {
|
|
231
|
+
rb_ary_push(query_string_tmp, rb_str_new2(qsk));
|
|
232
|
+
qsk = strtok(NULL, "&\0");
|
|
233
|
+
}
|
|
234
|
+
int qslen = RARRAY_LEN(query_string_tmp);
|
|
235
|
+
int qsi;
|
|
236
|
+
for (qsi = 0; qsi < qslen; qsi++) {
|
|
237
|
+
qsk = strtok(RSTRING_PTR(rb_ary_entry(query_string_tmp, qsi)), "=\0");
|
|
238
|
+
qsv = strtok(NULL, "\0");
|
|
239
|
+
if (qsv != NULL) {
|
|
240
|
+
rb_hash_aset(query_string, rb_str_new2(qsk), rb_str_new2(qsv));
|
|
241
|
+
}
|
|
242
|
+
else if(qsk != NULL && qsv != NULL) {
|
|
243
|
+
rb_hash_aset(query_string, rb_str_new2(qsk), rb_str_new2(""));
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return query_string;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* A method that is compatible with the rack api.
|
|
252
|
+
*/
|
|
253
|
+
static VALUE statsrb_rack_call(VALUE self, VALUE env) {
|
|
254
|
+
VALUE response = rb_ary_new();
|
|
255
|
+
VALUE headers = rb_hash_new();
|
|
256
|
+
VALUE body = rb_ary_new();
|
|
257
|
+
VALUE statsrb_data = rb_iv_get(self, "@data");
|
|
258
|
+
VALUE statsrb_hash = rb_hash_new();
|
|
259
|
+
|
|
260
|
+
// @data hash key symbols.
|
|
261
|
+
VALUE statsrb_key_ts = rb_iv_get(self, "@key_ts");
|
|
262
|
+
VALUE statsrb_key_ns = rb_iv_get(self, "@key_ns");
|
|
263
|
+
VALUE statsrb_key_v = rb_iv_get(self, "@key_v");
|
|
264
|
+
|
|
265
|
+
char *path = RSTRING_PTR(rb_hash_aref(env, rb_str_new2("PATH_INFO")));
|
|
266
|
+
|
|
267
|
+
rb_hash_aset(headers, rb_str_new2("Content-Type"), rb_str_new2("text/json"));
|
|
268
|
+
|
|
269
|
+
// Parse the query string
|
|
270
|
+
char *qs = RSTRING_PTR(rb_hash_aref(env, rb_str_new2("QUERY_STRING")));
|
|
271
|
+
VALUE query_string = statsrb_parse_qs(qs);
|
|
272
|
+
|
|
273
|
+
//const char *method = RSTRING_PTR(rb_hash_aref(env, rb_str_new2("REQUEST_METHOD")));
|
|
274
|
+
// @TODO consider moving the request method to the proper REQUEST_METHOD
|
|
275
|
+
const char *method_get = "GET";
|
|
276
|
+
const char *method_put = "PUT";
|
|
277
|
+
// Remove the leading /
|
|
278
|
+
path++;
|
|
279
|
+
const char *method = strtok(path, "/\0");
|
|
280
|
+
if (method && strcmp(method, method_put) == 0) {
|
|
281
|
+
long int statsrb_ts, statsrb_v;
|
|
282
|
+
|
|
283
|
+
// Get the timestamp, default to now.
|
|
284
|
+
VALUE statsrb_ts_qs = rb_hash_aref(query_string, rb_str_new("time", 4));
|
|
285
|
+
if (statsrb_ts_qs != Qnil) {
|
|
286
|
+
statsrb_ts = atoi(RSTRING_PTR(statsrb_ts_qs ));
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
statsrb_ts = (long int)time(NULL);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Get the namespace.
|
|
293
|
+
VALUE statsrb_ns = rb_hash_aref(query_string, rb_str_new("name", 4));
|
|
294
|
+
if (statsrb_ns == Qnil) {
|
|
295
|
+
statsrb_ns = NULL;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (statsrb_ns) {
|
|
299
|
+
// Get the value.
|
|
300
|
+
statsrb_v= 0;
|
|
301
|
+
VALUE statsrb_v_qs = rb_hash_aref(query_string, rb_str_new("value", 5));
|
|
302
|
+
if (statsrb_v_qs != Qnil) {
|
|
303
|
+
statsrb_v = atoi(RSTRING_PTR(statsrb_v_qs));
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
rb_hash_aset(statsrb_hash, statsrb_key_ts, INT2NUM(statsrb_ts));
|
|
307
|
+
rb_hash_aset(statsrb_hash, statsrb_key_ns, statsrb_ns);
|
|
308
|
+
rb_hash_aset(statsrb_hash, statsrb_key_v, INT2NUM(statsrb_v));
|
|
309
|
+
rb_ary_push(statsrb_data, statsrb_hash);
|
|
310
|
+
|
|
311
|
+
int data_length = RARRAY_LEN(statsrb_data);
|
|
312
|
+
rb_ary_push(body, rb_obj_as_string(INT2NUM(RARRAY_LEN(statsrb_data))));
|
|
313
|
+
if (data_length > 9) {
|
|
314
|
+
statsrb_sort(self);
|
|
315
|
+
statsrb_split_write(self, rb_iv_get(self, "@split_file_dir"), rb_str_new2("a+"));
|
|
316
|
+
rb_ary_resize(statsrb_data, 0);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
rb_ary_push(body, statsrb_ns);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
else if (method && strcmp(method, method_get) == 0) {
|
|
323
|
+
const char * statsrb_str_ns = strtok(NULL, "/\0");
|
|
324
|
+
if (statsrb_str_ns == NULL) {
|
|
325
|
+
statsrb_str_ns = "data";
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
VALUE jsoncallback = rb_hash_aref(query_string, rb_str_new("jsoncallback", 12));
|
|
329
|
+
if (jsoncallback != Qnil) {
|
|
330
|
+
rb_ary_push(body, rb_str_plus(jsoncallback, rb_str_new("(", 1)));
|
|
331
|
+
}
|
|
332
|
+
char json_start[256];
|
|
333
|
+
sprintf(json_start, "{\"%s\":[", statsrb_str_ns);
|
|
334
|
+
rb_ary_push(body, rb_str_new2(json_start));
|
|
335
|
+
|
|
336
|
+
// If they didn't specify a namespace, bail out immediately.
|
|
337
|
+
if (statsrb_str_ns) {
|
|
338
|
+
VALUE statsrb_ns = rb_str_new2(statsrb_str_ns);
|
|
339
|
+
long int query_limit, query_start, query_end;
|
|
340
|
+
|
|
341
|
+
// Get the query limit.
|
|
342
|
+
query_limit = 100;
|
|
343
|
+
VALUE query_limit_qs = rb_hash_aref(query_string, rb_str_new("limit", 5));
|
|
344
|
+
if (query_limit_qs != Qnil) {
|
|
345
|
+
query_limit = atoi(RSTRING_PTR(query_limit_qs));
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// Get the query start.
|
|
349
|
+
query_start = 0;
|
|
350
|
+
VALUE query_start_qs = rb_hash_aref(query_string, rb_str_new("start", 5));
|
|
351
|
+
if (query_start_qs != Qnil) {
|
|
352
|
+
query_start = atoi(RSTRING_PTR(query_start_qs));
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Get the query end.
|
|
356
|
+
query_end = 0;
|
|
357
|
+
VALUE query_end_qs = rb_hash_aref(query_string, rb_str_new("end", 3));
|
|
358
|
+
if (query_end_qs != Qnil) {
|
|
359
|
+
query_end = atoi(RSTRING_PTR(query_end_qs));
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Get the past N seconds of data.
|
|
363
|
+
// @TODO the query method fails if we query for data newer than the last entry.
|
|
364
|
+
VALUE query_recent = rb_hash_aref(query_string, rb_str_new("recent", 6));
|
|
365
|
+
if (query_recent != Qnil) {
|
|
366
|
+
query_end = (long int)time(NULL);
|
|
367
|
+
long int history = atoi(RSTRING_PTR(query_recent));
|
|
368
|
+
query_start = query_end - history;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Create a new Statsrb object to query from.
|
|
372
|
+
// @todo we probably need to assign a new array to @data to avoid messing up the pointers.
|
|
373
|
+
VALUE tmp = rb_obj_dup(self);
|
|
374
|
+
VALUE tmp_data = rb_ary_new();
|
|
375
|
+
rb_iv_set(tmp, "@data", tmp_data);
|
|
376
|
+
statsrb_query(tmp, rb_str_plus(rb_iv_get(self, "@split_file_dir"), statsrb_ns), statsrb_ns, INT2NUM(query_limit), INT2NUM(query_start), INT2NUM(query_end));
|
|
377
|
+
statsrb_sort(tmp);
|
|
378
|
+
|
|
379
|
+
int i, data_length = RARRAY_LEN(tmp_data);
|
|
380
|
+
|
|
381
|
+
for (i = 0; i < data_length; i++) {
|
|
382
|
+
rb_ary_push(body, rb_str_new("[", 1));
|
|
383
|
+
rb_ary_push(body, rb_obj_as_string(rb_hash_aref(rb_ary_entry(tmp_data, i), statsrb_key_ts )));
|
|
384
|
+
rb_ary_push(body, rb_str_new(",", 1));
|
|
385
|
+
rb_ary_push(body, rb_obj_as_string(rb_hash_aref(rb_ary_entry(tmp_data, i), statsrb_key_v )));
|
|
386
|
+
rb_ary_push(body, rb_str_new("]", 1));
|
|
387
|
+
if (i < data_length - 1) {
|
|
388
|
+
rb_ary_push(body, rb_str_new(",", 1));
|
|
389
|
+
}
|
|
390
|
+
rb_ary_push(body, rb_str_new("\n", 1));
|
|
391
|
+
}
|
|
392
|
+
rb_ary_resize(tmp_data, 0);
|
|
393
|
+
}
|
|
394
|
+
rb_ary_push(body, rb_str_new("]}", 2));
|
|
395
|
+
if (jsoncallback != Qnil) {
|
|
396
|
+
rb_ary_push(body, rb_str_new(")", 1));
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
else {
|
|
400
|
+
rb_ary_push(response, INT2NUM(404));
|
|
401
|
+
rb_ary_push(response, headers);
|
|
402
|
+
rb_ary_push(response, body);
|
|
403
|
+
return response;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
rb_ary_push(response, INT2NUM(200));
|
|
407
|
+
rb_ary_push(response, headers);
|
|
408
|
+
rb_ary_push(response, body);
|
|
409
|
+
|
|
410
|
+
return response;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Class constructor, sets up an instance variable.
|
|
415
|
+
*/
|
|
416
|
+
static VALUE statsrb_constructor(VALUE self) {
|
|
417
|
+
VALUE statsrb_data = rb_ary_new();
|
|
418
|
+
rb_iv_set(self, "@data", statsrb_data);
|
|
419
|
+
VALUE statsrb_split_file_dir = rb_str_new("/tmp", 4);
|
|
420
|
+
rb_iv_set(self, "@split_file_dir", statsrb_split_file_dir);
|
|
421
|
+
|
|
422
|
+
// Internal symbols for :ts, :ns and :v.
|
|
423
|
+
VALUE statsrb_key_ts = rb_str_intern(rb_str_new2("ts"));
|
|
424
|
+
rb_iv_set(self, "@key_ts", statsrb_key_ts);
|
|
425
|
+
VALUE statsrb_key_ns = rb_str_intern(rb_str_new2("ns"));
|
|
426
|
+
rb_iv_set(self, "@key_ns", statsrb_key_ns);
|
|
427
|
+
VALUE statsrb_key_v = rb_str_intern(rb_str_new2("v"));
|
|
428
|
+
rb_iv_set(self, "@key_v", statsrb_key_v);
|
|
429
|
+
|
|
430
|
+
return self;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Init the Statsrb class.
|
|
435
|
+
*/
|
|
436
|
+
void Init_statsrb(void) {
|
|
437
|
+
VALUE klass = rb_define_class("Statsrb", rb_cObject);
|
|
438
|
+
|
|
439
|
+
// Instance methods and properties.
|
|
440
|
+
rb_define_method(klass, "initialize", statsrb_constructor, 0);
|
|
441
|
+
rb_define_method(klass, "query", statsrb_query, 5);
|
|
442
|
+
rb_define_method(klass, "sort", statsrb_sort, 0);
|
|
443
|
+
rb_define_method(klass, "write", statsrb_write, 2);
|
|
444
|
+
rb_define_method(klass, "split_write", statsrb_split_write, 2);
|
|
445
|
+
rb_define_method(klass, "call", statsrb_rack_call, 1);
|
|
446
|
+
// Define :attr_accessor (read/write instance var)
|
|
447
|
+
// Note that this must correspond with a call to rb_iv_self() and it's string name must be @data.
|
|
448
|
+
rb_define_attr(klass, "data", 1, 1);
|
|
449
|
+
rb_define_attr(klass, "split_file_dir", 1, 1);
|
|
450
|
+
}
|
data/lib/statsrb.rb
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
require 'statsrb/statsrb'
|
|
2
|
+
|
|
3
|
+
# @author Kevin Hankens
|
|
4
|
+
class Statsrb
|
|
5
|
+
# [!{:ts => Time.now.to_i, :ns => "test", :v => 33}]
|
|
6
|
+
attr_accessor :data
|
|
7
|
+
|
|
8
|
+
# Writes the @data in memory to a specified file.
|
|
9
|
+
# @param filepath [String]
|
|
10
|
+
# @param filemode [String]
|
|
11
|
+
# @return Statsrb
|
|
12
|
+
def write filepath, filemode
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Splits namespaces in @data in memory to a separate files.
|
|
16
|
+
# @param filepath [String]
|
|
17
|
+
# @param filemode [String]
|
|
18
|
+
# @return Statsrb
|
|
19
|
+
def split_write filepath, filemode
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Locates data from a specified file and loads into @data.
|
|
23
|
+
# @param filepath [String]
|
|
24
|
+
# @param namespace [String]
|
|
25
|
+
# @param limit [Number]
|
|
26
|
+
# @param start_time [Number]
|
|
27
|
+
# @param end_time [Number]
|
|
28
|
+
# @return Statsrb
|
|
29
|
+
def query filepath, namespace, limit, start_time, end_time
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Returns a rack-compatable response.
|
|
33
|
+
# @param env [Hash]
|
|
34
|
+
def sort env
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: statsrb
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
prerelease:
|
|
6
|
+
platform: ruby
|
|
7
|
+
authors:
|
|
8
|
+
- Kevin Hankens
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
date: 2013-06-08 00:00:00.000000000 Z
|
|
13
|
+
dependencies: []
|
|
14
|
+
description: A ruby stats repository.
|
|
15
|
+
email: email@kevinhankens.com
|
|
16
|
+
executables: []
|
|
17
|
+
extensions:
|
|
18
|
+
- ext/statsrb/extconf.rb
|
|
19
|
+
extra_rdoc_files: []
|
|
20
|
+
files:
|
|
21
|
+
- lib/statsrb.rb
|
|
22
|
+
- ext/statsrb/statsrb.c
|
|
23
|
+
- ext/statsrb/extconf.rb
|
|
24
|
+
homepage: https://github.com/kevinhankens/statsrb
|
|
25
|
+
licenses: []
|
|
26
|
+
post_install_message:
|
|
27
|
+
rdoc_options: []
|
|
28
|
+
require_paths:
|
|
29
|
+
- lib
|
|
30
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
31
|
+
none: false
|
|
32
|
+
requirements:
|
|
33
|
+
- - ! '>='
|
|
34
|
+
- !ruby/object:Gem::Version
|
|
35
|
+
version: '0'
|
|
36
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
37
|
+
none: false
|
|
38
|
+
requirements:
|
|
39
|
+
- - ! '>='
|
|
40
|
+
- !ruby/object:Gem::Version
|
|
41
|
+
version: '0'
|
|
42
|
+
requirements: []
|
|
43
|
+
rubyforge_project:
|
|
44
|
+
rubygems_version: 1.8.25
|
|
45
|
+
signing_key:
|
|
46
|
+
specification_version: 3
|
|
47
|
+
summary: Statsrb
|
|
48
|
+
test_files: []
|
|
49
|
+
has_rdoc:
|