rage-iodine 1.7.58

Sign up to get free protection for your applications and to get access to all the features.
Files changed (128) hide show
  1. checksums.yaml +7 -0
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
  3. data/.github/workflows/ruby.yml +42 -0
  4. data/.gitignore +20 -0
  5. data/.rspec +2 -0
  6. data/.yardopts +8 -0
  7. data/CHANGELOG.md +1098 -0
  8. data/Gemfile +11 -0
  9. data/LICENSE.txt +21 -0
  10. data/LIMITS.md +41 -0
  11. data/README.md +782 -0
  12. data/Rakefile +23 -0
  13. data/SPEC-PubSub-Draft.md +159 -0
  14. data/SPEC-WebSocket-Draft.md +239 -0
  15. data/bin/console +22 -0
  16. data/bin/info.md +353 -0
  17. data/bin/mustache_bench.rb +100 -0
  18. data/bin/poc/Gemfile.lock +23 -0
  19. data/bin/poc/README.md +37 -0
  20. data/bin/poc/config.ru +66 -0
  21. data/bin/poc/gemfile +1 -0
  22. data/bin/poc/www/index.html +57 -0
  23. data/examples/async_task.ru +92 -0
  24. data/examples/bates/README.md +3 -0
  25. data/examples/bates/config.ru +342 -0
  26. data/examples/bates/david+bold.pdf +0 -0
  27. data/examples/bates/public/drop-pdf.png +0 -0
  28. data/examples/bates/public/index.html +600 -0
  29. data/examples/config.ru +59 -0
  30. data/examples/echo.ru +59 -0
  31. data/examples/etag.ru +16 -0
  32. data/examples/hello.ru +29 -0
  33. data/examples/pubsub_engine.ru +81 -0
  34. data/examples/rack3.ru +12 -0
  35. data/examples/redis.ru +70 -0
  36. data/examples/shootout.ru +73 -0
  37. data/examples/sub-protocols.ru +90 -0
  38. data/examples/tcp_client.rb +66 -0
  39. data/examples/x-sendfile.ru +14 -0
  40. data/exe/iodine +280 -0
  41. data/ext/iodine/extconf.rb +110 -0
  42. data/ext/iodine/fio.c +12096 -0
  43. data/ext/iodine/fio.h +6390 -0
  44. data/ext/iodine/fio_cli.c +431 -0
  45. data/ext/iodine/fio_cli.h +189 -0
  46. data/ext/iodine/fio_json_parser.h +687 -0
  47. data/ext/iodine/fio_siphash.c +157 -0
  48. data/ext/iodine/fio_siphash.h +37 -0
  49. data/ext/iodine/fio_tls.h +129 -0
  50. data/ext/iodine/fio_tls_missing.c +649 -0
  51. data/ext/iodine/fio_tls_openssl.c +1056 -0
  52. data/ext/iodine/fio_tmpfile.h +50 -0
  53. data/ext/iodine/fiobj.h +44 -0
  54. data/ext/iodine/fiobj4fio.h +21 -0
  55. data/ext/iodine/fiobj_ary.c +333 -0
  56. data/ext/iodine/fiobj_ary.h +139 -0
  57. data/ext/iodine/fiobj_data.c +1185 -0
  58. data/ext/iodine/fiobj_data.h +167 -0
  59. data/ext/iodine/fiobj_hash.c +409 -0
  60. data/ext/iodine/fiobj_hash.h +176 -0
  61. data/ext/iodine/fiobj_json.c +622 -0
  62. data/ext/iodine/fiobj_json.h +68 -0
  63. data/ext/iodine/fiobj_mem.h +71 -0
  64. data/ext/iodine/fiobj_mustache.c +317 -0
  65. data/ext/iodine/fiobj_mustache.h +62 -0
  66. data/ext/iodine/fiobj_numbers.c +344 -0
  67. data/ext/iodine/fiobj_numbers.h +127 -0
  68. data/ext/iodine/fiobj_str.c +433 -0
  69. data/ext/iodine/fiobj_str.h +172 -0
  70. data/ext/iodine/fiobject.c +620 -0
  71. data/ext/iodine/fiobject.h +654 -0
  72. data/ext/iodine/hpack.h +1923 -0
  73. data/ext/iodine/http.c +2736 -0
  74. data/ext/iodine/http.h +1019 -0
  75. data/ext/iodine/http1.c +825 -0
  76. data/ext/iodine/http1.h +29 -0
  77. data/ext/iodine/http1_parser.h +1835 -0
  78. data/ext/iodine/http_internal.c +1279 -0
  79. data/ext/iodine/http_internal.h +248 -0
  80. data/ext/iodine/http_mime_parser.h +350 -0
  81. data/ext/iodine/iodine.c +1433 -0
  82. data/ext/iodine/iodine.h +64 -0
  83. data/ext/iodine/iodine_caller.c +218 -0
  84. data/ext/iodine/iodine_caller.h +27 -0
  85. data/ext/iodine/iodine_connection.c +941 -0
  86. data/ext/iodine/iodine_connection.h +55 -0
  87. data/ext/iodine/iodine_defer.c +420 -0
  88. data/ext/iodine/iodine_defer.h +6 -0
  89. data/ext/iodine/iodine_fiobj2rb.h +120 -0
  90. data/ext/iodine/iodine_helpers.c +282 -0
  91. data/ext/iodine/iodine_helpers.h +12 -0
  92. data/ext/iodine/iodine_http.c +1280 -0
  93. data/ext/iodine/iodine_http.h +23 -0
  94. data/ext/iodine/iodine_json.c +302 -0
  95. data/ext/iodine/iodine_json.h +6 -0
  96. data/ext/iodine/iodine_mustache.c +567 -0
  97. data/ext/iodine/iodine_mustache.h +6 -0
  98. data/ext/iodine/iodine_pubsub.c +580 -0
  99. data/ext/iodine/iodine_pubsub.h +26 -0
  100. data/ext/iodine/iodine_rack_io.c +273 -0
  101. data/ext/iodine/iodine_rack_io.h +20 -0
  102. data/ext/iodine/iodine_store.c +142 -0
  103. data/ext/iodine/iodine_store.h +20 -0
  104. data/ext/iodine/iodine_tcp.c +346 -0
  105. data/ext/iodine/iodine_tcp.h +13 -0
  106. data/ext/iodine/iodine_tls.c +261 -0
  107. data/ext/iodine/iodine_tls.h +13 -0
  108. data/ext/iodine/mustache_parser.h +1546 -0
  109. data/ext/iodine/redis_engine.c +957 -0
  110. data/ext/iodine/redis_engine.h +79 -0
  111. data/ext/iodine/resp_parser.h +317 -0
  112. data/ext/iodine/scheduler.c +173 -0
  113. data/ext/iodine/scheduler.h +6 -0
  114. data/ext/iodine/websocket_parser.h +506 -0
  115. data/ext/iodine/websockets.c +752 -0
  116. data/ext/iodine/websockets.h +185 -0
  117. data/iodine.gemspec +50 -0
  118. data/lib/iodine/connection.rb +61 -0
  119. data/lib/iodine/json.rb +42 -0
  120. data/lib/iodine/mustache.rb +113 -0
  121. data/lib/iodine/pubsub.rb +55 -0
  122. data/lib/iodine/rack_utils.rb +43 -0
  123. data/lib/iodine/tls.rb +16 -0
  124. data/lib/iodine/version.rb +3 -0
  125. data/lib/iodine.rb +274 -0
  126. data/lib/rack/handler/iodine.rb +33 -0
  127. data/logo.png +0 -0
  128. metadata +284 -0
@@ -0,0 +1,567 @@
1
+ #include <ruby.h>
2
+ #define INCLUDE_MUSTACHE_IMPLEMENTATION 1
3
+ #include "mustache_parser.h"
4
+
5
+ #include "iodine.h"
6
+
7
+ #define FIO_INCLUDE_STR
8
+ #include <fio.h>
9
+
10
+ static ID call_func_id;
11
+ static VALUE filename_id;
12
+ static VALUE data_id;
13
+ static VALUE template_id;
14
+ /* *****************************************************************************
15
+ C <=> Ruby Data allocation
16
+ ***************************************************************************** */
17
+
18
+ static size_t iodine_mustache_data_size(const void *c_) {
19
+ return sizeof(mustache_s *) +
20
+ (((mustache_s **)c_)[0]
21
+ ? (((mustache_s **)c_)[0]->u.read_only.data_length +
22
+ (((mustache_s **)c_)[0]->u.read_only.intruction_count *
23
+ sizeof(struct mustache__instruction_s)))
24
+ : 0);
25
+ (void)c_;
26
+ }
27
+
28
+ static void iodine_mustache_data_free(void *c_) {
29
+ mustache_free(((mustache_s **)c_)[0]);
30
+ FIO_LOG_DEBUG("deallocated mustache data at: %p", ((void **)c_)[0]);
31
+ free((void *)c_);
32
+ FIO_LOG_DEBUG("deallocated mustache pointer at: %p", c_);
33
+ (void)c_;
34
+ }
35
+
36
+ static const rb_data_type_t iodine_mustache_data_type = {
37
+ .wrap_struct_name = "IodineMustacheData",
38
+ .function =
39
+ {
40
+ .dmark = NULL,
41
+ .dfree = iodine_mustache_data_free,
42
+ .dsize = iodine_mustache_data_size,
43
+ },
44
+ .data = NULL,
45
+ // .flags = RUBY_TYPED_FREE_IMMEDIATELY,
46
+ };
47
+
48
+ /* Iodine::PubSub::Engine.allocate */
49
+ static VALUE iodine_mustache_data_alloc_c(VALUE self) {
50
+ void *m = malloc(sizeof(mustache_s *));
51
+ ((mustache_s **)m)[0] = NULL;
52
+ FIO_LOG_DEBUG("allocated mustache pointer at: %p", m);
53
+ return TypedData_Wrap_Struct(self, &iodine_mustache_data_type, m);
54
+ }
55
+
56
+ /* *****************************************************************************
57
+ Parser Callbacks
58
+ ***************************************************************************** */
59
+
60
+ static inline VALUE fiobj_mustache_find_obj_absolute(VALUE udata,
61
+ const char *name,
62
+ uint32_t name_len) {
63
+ VALUE tmp;
64
+ if (!RB_TYPE_P(udata, T_HASH)) {
65
+ if (name_len == 1 && name[0] == '.')
66
+ return udata;
67
+ /* search by method */
68
+ ID name_id = rb_intern2(name, name_len);
69
+ if (rb_respond_to(udata, name_id)) {
70
+ return IodineCaller.call(udata, name_id);
71
+ }
72
+ return Qnil;
73
+ }
74
+ /* search by Symbol */
75
+ ID name_id = rb_intern2(name, name_len);
76
+ VALUE key = ID2SYM(name_id);
77
+ tmp = rb_hash_lookup2(udata, key, Qundef);
78
+ if (tmp != Qundef)
79
+ return tmp;
80
+ /* search by String */
81
+ key = rb_sym2str(key);
82
+ tmp = rb_hash_lookup2(udata, key, Qundef);
83
+ rb_str_free(key);
84
+ if (tmp != Qundef)
85
+ return tmp;
86
+ /* search by method */
87
+ tmp = Qnil;
88
+ if (rb_respond_to(udata, name_id)) {
89
+ tmp = IodineCaller.call(udata, name_id);
90
+ }
91
+
92
+ return tmp;
93
+ }
94
+
95
+ static inline VALUE fiobj_mustache_find_obj_tree(mustache_section_s *section,
96
+ const char *name,
97
+ uint32_t name_len) {
98
+ do {
99
+ VALUE tmp = fiobj_mustache_find_obj_absolute((VALUE)section->udata2, name,
100
+ name_len);
101
+ if (tmp != Qnil) {
102
+ return tmp;
103
+ }
104
+ } while ((section = mustache_section_parent(section)));
105
+ return Qnil;
106
+ }
107
+
108
+ static inline VALUE fiobj_mustache_find_obj(mustache_section_s *section,
109
+ const char *name,
110
+ uint32_t name_len) {
111
+ VALUE tmp = fiobj_mustache_find_obj_tree(section, name, name_len);
112
+ if (tmp != Qnil)
113
+ return tmp;
114
+ /* interpolate sections... */
115
+ uint32_t dot = 0;
116
+ while (dot < name_len && name[dot] != '.')
117
+ ++dot;
118
+ if (dot == name_len)
119
+ return Qnil;
120
+ tmp = fiobj_mustache_find_obj_tree(section, name, dot);
121
+ if (!tmp) {
122
+ return Qnil;
123
+ }
124
+ ++dot;
125
+ for (;;) {
126
+ VALUE obj =
127
+ fiobj_mustache_find_obj_absolute(tmp, name + dot, name_len - dot);
128
+ if (obj != Qnil)
129
+ return obj;
130
+ name += dot;
131
+ name_len -= dot;
132
+ dot = 0;
133
+ while (dot < name_len && name[dot] != '.')
134
+ ++dot;
135
+ if (dot == name_len) {
136
+ return Qnil;
137
+ }
138
+ tmp = fiobj_mustache_find_obj_absolute(tmp, name, dot);
139
+ if (tmp == Qnil)
140
+ return Qnil;
141
+ ++dot;
142
+ }
143
+ }
144
+ /**
145
+ * Called when an argument name was detected in the current section.
146
+ *
147
+ * A conforming implementation will search for the named argument both in the
148
+ * existing section and all of it's parents (walking backwards towards the root)
149
+ * until a value is detected.
150
+ *
151
+ * A missing value should be treated the same as an empty string.
152
+ *
153
+ * A conforming implementation will output the named argument's value (either
154
+ * HTML escaped or not, depending on the `escape` flag) as a string.
155
+ */
156
+ static int mustache_on_arg(mustache_section_s *section, const char *name,
157
+ uint32_t name_len, unsigned char escape) {
158
+ VALUE o = fiobj_mustache_find_obj(section, name, name_len);
159
+ switch (o) {
160
+ case Qnil:
161
+ case Qfalse:
162
+ return 0;
163
+ case Qtrue:
164
+ fio_str_write(section->udata1, "true", 4);
165
+ break;
166
+ }
167
+ if (!RB_TYPE_P(o, T_STRING)) {
168
+ if (rb_respond_to(o, call_func_id))
169
+ o = IodineCaller.call(o, call_func_id);
170
+ if (!RB_TYPE_P(o, T_STRING))
171
+ o = IodineCaller.call(o, iodine_to_s_id);
172
+ }
173
+ if (!RB_TYPE_P(o, T_STRING) || !RSTRING_LEN(o))
174
+ return 0;
175
+ return mustache_write_text(section, RSTRING_PTR(o), RSTRING_LEN(o), escape);
176
+ }
177
+
178
+ /**
179
+ * Called when simple template text (string) is detected.
180
+ *
181
+ * A conforming implementation will output data as a string (no escaping).
182
+ */
183
+ static int mustache_on_text(mustache_section_s *section, const char *data,
184
+ uint32_t data_len) {
185
+ fio_str_write(section->udata1, data, data_len);
186
+ return 0;
187
+ }
188
+
189
+ /**
190
+ * Called for nested sections, must return the number of objects in the new
191
+ * subsection (depending on the argument's name).
192
+ *
193
+ * Arrays should return the number of objects in the array.
194
+ *
195
+ * `true` values should return 1.
196
+ *
197
+ * `false` values should return 0.
198
+ *
199
+ * A return value of -1 will stop processing with an error.
200
+ *
201
+ * Please note, this will handle both normal and inverted sections.
202
+ */
203
+ static int32_t mustache_on_section_test(mustache_section_s *section,
204
+ const char *name, uint32_t name_len,
205
+ uint8_t callable) {
206
+ VALUE o = fiobj_mustache_find_obj(section, name, name_len);
207
+ if (o == Qnil || o == Qfalse) {
208
+ return 0;
209
+ }
210
+ if (RB_TYPE_P(o, T_ARRAY)) {
211
+ return RARRAY_LEN(o);
212
+ }
213
+ if (callable && rb_respond_to(o, call_func_id)) {
214
+ size_t len;
215
+ const char *txt = mustache_section_text(section, &len);
216
+ VALUE str = Qnil;
217
+ if (txt && len) {
218
+ str = rb_str_new(txt, len);
219
+ }
220
+ o = IodineCaller.call2(o, call_func_id, 1, &str);
221
+ if (!RB_TYPE_P(o, T_STRING))
222
+ o = rb_funcall2(o, iodine_to_s_id, 0, NULL);
223
+ if (RB_TYPE_P(o, T_STRING) && RSTRING_LEN(o))
224
+ mustache_write_text(section, RSTRING_PTR(o), RSTRING_LEN(o), 0);
225
+ return 0;
226
+ }
227
+ return 1;
228
+ }
229
+
230
+ /**
231
+ * Called when entering a nested section.
232
+ *
233
+ * `index` is a zero based index indicating the number of repetitions that
234
+ * occurred so far (same as the array index for arrays).
235
+ *
236
+ * A return value of -1 will stop processing with an error.
237
+ *
238
+ * Note: this is a good time to update the subsection's `udata` with the value
239
+ * of the array index. The `udata` will always contain the value or the parent's
240
+ * `udata`.
241
+ */
242
+ static int mustache_on_section_start(mustache_section_s *section,
243
+ char const *name, uint32_t name_len,
244
+ uint32_t index) {
245
+ VALUE o = fiobj_mustache_find_obj(section, name, name_len);
246
+ if (RB_TYPE_P(o, T_ARRAY))
247
+ section->udata2 = (void *)rb_ary_entry(o, index);
248
+ else if (RB_TYPE_P(o, T_HASH))
249
+ section->udata2 = (void *)o;
250
+ return 0;
251
+ }
252
+
253
+ /**
254
+ * Called for cleanup in case of error.
255
+ */
256
+ static void mustache_on_formatting_error(void *udata1, void *udata2) {
257
+ (void)udata1;
258
+ (void)udata2;
259
+ }
260
+
261
+ /* *****************************************************************************
262
+ Loading the template
263
+ ***************************************************************************** */
264
+
265
+ /**
266
+ Loads the mustache template found in `:filename`. If `:template` is provided it
267
+ will be used instead of reading the file's content.
268
+
269
+ Iodine::Mustache.new(filename, template = nil)
270
+
271
+ When template data is provided, filename (if any) will only be used for partial
272
+ template path resolution and the template data will be used for the template's
273
+ content. This allows, for example, for front matter to be extracted before
274
+ parsing the template.
275
+
276
+ Once a template was loaded, it could be rendered using {render}.
277
+
278
+ Accepts named arguments as well:
279
+
280
+ Iodine::Mustache.new(filename: "foo.mustache", template: "{{ bar }}")
281
+
282
+ */
283
+ static VALUE iodine_mustache_new(int argc, VALUE *argv, VALUE self) {
284
+ VALUE filename = Qnil, template = Qnil;
285
+ if (argc == 1 && RB_TYPE_P(argv[0], T_HASH)) {
286
+ /* named arguments */
287
+ filename = rb_hash_aref(argv[0], filename_id);
288
+ template = rb_hash_aref(argv[0], template_id);
289
+ } else {
290
+ /* regular arguments */
291
+ if (argc == 0 || argc > 2)
292
+ rb_raise(rb_eArgError, "expecting 1..2 arguments or named arguments.");
293
+ filename = argv[0];
294
+ if (argc > 1) {
295
+ template = argv[1];
296
+ }
297
+ }
298
+ if (filename == Qnil && template == Qnil)
299
+ rb_raise(rb_eArgError, "need either template contents or file name.");
300
+
301
+ if (template != Qnil)
302
+ Check_Type(template, T_STRING);
303
+ if (filename != Qnil)
304
+ Check_Type(filename, T_STRING);
305
+
306
+ mustache_s **m = NULL;
307
+ TypedData_Get_Struct(self, mustache_s *, &iodine_mustache_data_type, m);
308
+ if (!m) {
309
+ rb_raise(rb_eRuntimeError, "Iodine::Mustache allocation error.");
310
+ }
311
+
312
+ mustache_error_en err;
313
+ *m = mustache_load(.filename =
314
+ (filename == Qnil ? NULL : RSTRING_PTR(filename)),
315
+ .filename_len =
316
+ (filename == Qnil ? 0 : RSTRING_LEN(filename)),
317
+ .data = (template == Qnil ? NULL : RSTRING_PTR(template)),
318
+ .data_len = (template == Qnil ? 0 : RSTRING_LEN(template)),
319
+ .err = &err);
320
+ if (!*m)
321
+ goto error;
322
+
323
+ FIO_LOG_DEBUG("allocated / loaded mustache data at: %p", (void *)*m);
324
+
325
+ return self;
326
+ error:
327
+ switch (err) {
328
+ case MUSTACHE_OK:
329
+ rb_raise(rb_eRuntimeError, "Iodine::Mustache template ok, unknown error.");
330
+ break;
331
+ case MUSTACHE_ERR_TOO_DEEP:
332
+ rb_raise(rb_eRuntimeError, "Iodine::Mustache element nesting too deep.");
333
+ break;
334
+ case MUSTACHE_ERR_CLOSURE_MISMATCH:
335
+ rb_raise(rb_eRuntimeError,
336
+ "Iodine::Mustache template error, closure mismatch.");
337
+ break;
338
+ case MUSTACHE_ERR_FILE_NOT_FOUND:
339
+ rb_raise(rb_eLoadError, "Iodine::Mustache template not found.");
340
+ break;
341
+ case MUSTACHE_ERR_FILE_TOO_BIG:
342
+ rb_raise(rb_eLoadError, "Iodine::Mustache template too big.");
343
+ break;
344
+ case MUSTACHE_ERR_FILE_NAME_TOO_LONG:
345
+ rb_raise(rb_eRuntimeError, "Iodine::Mustache template name too long.");
346
+ break;
347
+ case MUSTACHE_ERR_EMPTY_TEMPLATE:
348
+ rb_raise(rb_eRuntimeError, "Iodine::Mustache template is empty.");
349
+ break;
350
+ case MUSTACHE_ERR_UNKNOWN:
351
+ rb_raise(rb_eRuntimeError, "Iodine::Mustache unknown error.");
352
+ break;
353
+ case MUSTACHE_ERR_USER_ERROR:
354
+ rb_raise(rb_eRuntimeError, "Iodine::Mustache internal error.");
355
+ break;
356
+ case MUSTACHE_ERR_FILE_NAME_TOO_SHORT:
357
+ rb_raise(rb_eRuntimeError, "Iodine::Mustache template file name too long.");
358
+
359
+ break;
360
+ case MUSTACHE_ERR_DELIMITER_TOO_LONG:
361
+ rb_raise(rb_eRuntimeError, "Iodine::Mustache new delimiter is too long.");
362
+
363
+ break;
364
+ case MUSTACHE_ERR_NAME_TOO_LONG:
365
+ rb_raise(rb_eRuntimeError,
366
+ "Iodine::Mustache section name in template is too long.");
367
+ default:
368
+ break;
369
+ }
370
+ return self;
371
+ }
372
+
373
+ /* *****************************************************************************
374
+ Rendering
375
+ ***************************************************************************** */
376
+
377
+ /**
378
+ Renders the mustache template using the data provided in the `data` argument.
379
+
380
+ Returns a String with the rendered template.
381
+
382
+ Raises an exception on error.
383
+
384
+ NOTE:
385
+
386
+ As one might notice, no binding is provided. Instead, a `data` Hash is assumed.
387
+ Iodine will search the Hash for any data while protecting against code
388
+ execution.
389
+ */
390
+ static VALUE iodine_mustache_render(VALUE self, VALUE data) {
391
+ fio_str_s str = FIO_STR_INIT;
392
+ mustache_s **m = NULL;
393
+ TypedData_Get_Struct(self, mustache_s *, &iodine_mustache_data_type, m);
394
+ if (!m) {
395
+ rb_raise(rb_eRuntimeError, "Iodine::Mustache allocation error.");
396
+ }
397
+ if (mustache_build(*m, .udata1 = &str, .udata2 = (void *)data))
398
+ goto error;
399
+ fio_str_info_s i = fio_str_info(&str);
400
+ VALUE ret = rb_str_new(i.data, i.len);
401
+ fio_str_free(&str);
402
+ return ret;
403
+
404
+ error:
405
+ fio_str_free(&str);
406
+ rb_raise(rb_eRuntimeError, "Couldn't build template frome data.");
407
+ }
408
+
409
+ /**
410
+ Renders the mustache template found in `filename`, using the data provided in
411
+ the `data` argument. If `template` is provided it will be used instead of
412
+ reading the file's content.
413
+
414
+ Iodine::Mustache.render(filename, data, template = nil)
415
+
416
+ Returns a String with the rendered template.
417
+
418
+ Raises an exception on error.
419
+
420
+ template = "<h1>{{title}}</h1>"
421
+ filename = "templates/index"
422
+ data = {title: "Home"}
423
+ result = Iodine::Mustache.render(filename, data)
424
+
425
+ # filename will be used to resolve the path to any partials:
426
+ result = Iodine::Mustache.render(filename, data, template)
427
+
428
+ # OR, if we don't need partial template path resolution
429
+ result = Iodine::Mustache.render(template: template, data: data)
430
+
431
+ NOTE 1:
432
+
433
+ This function doesn't cache the template data.
434
+
435
+ The more complext the template the higher the cost of the template parsing
436
+ stage.
437
+
438
+ Consider creating a persistent template object using a new object and using the
439
+ instance {#render} method.
440
+
441
+ NOTE 2:
442
+
443
+ As one might notice, no binding is provided. Instead, a `data` Hash is assumed.
444
+ Iodine will search the Hash for any data while protecting against code
445
+ execution.
446
+ */
447
+ static VALUE iodine_mustache_render_klass(int argc, VALUE *argv, VALUE self) {
448
+ VALUE filename = Qnil, data = Qnil, template = Qnil;
449
+ if (argc == 1) {
450
+ /* named arguments */
451
+ Check_Type(argv[0], T_HASH);
452
+ filename = rb_hash_aref(argv[0], filename_id);
453
+ data = rb_hash_aref(argv[0], data_id);
454
+ template = rb_hash_aref(argv[0], template_id);
455
+ } else {
456
+ /* regular arguments */
457
+ if (argc < 2 || argc > 3)
458
+ rb_raise(rb_eArgError, "expecting 2..3 arguments or named arguments.");
459
+ filename = argv[0];
460
+ data = argv[1];
461
+ if (argc > 2) {
462
+ template = argv[2];
463
+ }
464
+ }
465
+ if (filename == Qnil && template == Qnil)
466
+ rb_raise(rb_eArgError, "need either template contents or file name.");
467
+
468
+ if (template != Qnil)
469
+ Check_Type(template, T_STRING);
470
+ if (filename != Qnil)
471
+ Check_Type(filename, T_STRING);
472
+
473
+ fio_str_s str = FIO_STR_INIT;
474
+
475
+ mustache_s *m = NULL;
476
+ mustache_error_en err;
477
+ m = mustache_load(.filename =
478
+ (filename == Qnil ? NULL : RSTRING_PTR(filename)),
479
+ .filename_len =
480
+ (filename == Qnil ? 0 : RSTRING_LEN(filename)),
481
+ .data = (template == Qnil ? NULL : RSTRING_PTR(template)),
482
+ .data_len = (template == Qnil ? 0 : RSTRING_LEN(template)),
483
+ .err = &err);
484
+ if (!m)
485
+ goto error;
486
+ int e = mustache_build(m, .udata1 = &str, .udata2 = (void *)data);
487
+ mustache_free(m);
488
+ if (e)
489
+ goto render_error;
490
+ fio_str_info_s i = fio_str_info(&str);
491
+ VALUE ret = rb_str_new(i.data, i.len);
492
+ fio_str_free(&str);
493
+ return ret;
494
+
495
+ error:
496
+ switch (err) {
497
+ case MUSTACHE_OK:
498
+ rb_raise(rb_eRuntimeError, "Iodine::Mustache template ok, unknown error.");
499
+ break;
500
+ case MUSTACHE_ERR_TOO_DEEP:
501
+ rb_raise(rb_eRuntimeError, "Iodine::Mustache element nesting too deep.");
502
+ break;
503
+ case MUSTACHE_ERR_CLOSURE_MISMATCH:
504
+ rb_raise(rb_eRuntimeError,
505
+ "Iodine::Mustache template error, closure mismatch.");
506
+ break;
507
+ case MUSTACHE_ERR_FILE_NOT_FOUND:
508
+ rb_raise(rb_eLoadError, "Iodine::Mustache template not found.");
509
+ break;
510
+ case MUSTACHE_ERR_FILE_TOO_BIG:
511
+ rb_raise(rb_eLoadError, "Iodine::Mustache template too big.");
512
+ break;
513
+ case MUSTACHE_ERR_FILE_NAME_TOO_LONG:
514
+ rb_raise(rb_eRuntimeError, "Iodine::Mustache template name too long.");
515
+ break;
516
+ case MUSTACHE_ERR_EMPTY_TEMPLATE:
517
+ rb_raise(rb_eRuntimeError, "Iodine::Mustache template is empty.");
518
+ break;
519
+ case MUSTACHE_ERR_UNKNOWN:
520
+ rb_raise(rb_eRuntimeError, "Iodine::Mustache unknown error.");
521
+ break;
522
+ case MUSTACHE_ERR_USER_ERROR:
523
+ rb_raise(rb_eRuntimeError,
524
+ "Iodine::Mustache internal error or unexpected data structure.");
525
+ break;
526
+ case MUSTACHE_ERR_FILE_NAME_TOO_SHORT:
527
+ rb_raise(rb_eRuntimeError, "Iodine::Mustache template file name too long.");
528
+
529
+ break;
530
+ case MUSTACHE_ERR_DELIMITER_TOO_LONG:
531
+ rb_raise(rb_eRuntimeError, "Iodine::Mustache new delimiter is too long.");
532
+
533
+ break;
534
+ case MUSTACHE_ERR_NAME_TOO_LONG:
535
+ rb_raise(rb_eRuntimeError,
536
+ "Iodine::Mustache section name in template is too long.");
537
+
538
+ break;
539
+ default:
540
+ break;
541
+ }
542
+ return Qnil;
543
+
544
+ render_error:
545
+ fio_str_free(&str);
546
+ rb_raise(rb_eRuntimeError, "Couldn't build template frome data.");
547
+ }
548
+
549
+ /* *****************************************************************************
550
+ Initialize Iodine::Mustache
551
+ ***************************************************************************** */
552
+
553
+ void iodine_init_mustache(void) {
554
+ call_func_id = rb_intern2("call", 4);
555
+ filename_id = rb_id2sym(rb_intern2("filename", 8));
556
+ data_id = rb_id2sym(rb_intern2("data", 4));
557
+ template_id = rb_id2sym(rb_intern2("template", 8));
558
+ rb_global_variable(&filename_id);
559
+ rb_global_variable(&data_id);
560
+ rb_global_variable(&template_id);
561
+ VALUE tmp = rb_define_class_under(IodineModule, "Mustache", rb_cObject);
562
+ rb_define_alloc_func(tmp, iodine_mustache_data_alloc_c);
563
+ rb_define_method(tmp, "initialize", iodine_mustache_new, -1);
564
+ rb_define_method(tmp, "render", iodine_mustache_render, 1);
565
+ rb_define_singleton_method(tmp, "render", iodine_mustache_render_klass, -1);
566
+ // rb_define_module_function(tmp, "render", iodine_mustache_render_klass, 2);
567
+ }
@@ -0,0 +1,6 @@
1
+ #ifndef H_IODINE_MUSTACHE_H
2
+ #define H_IODINE_MUSTACHE_H
3
+
4
+ void iodine_init_mustache(void);
5
+
6
+ #endif